import os
import sys
sys.path.extend([os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))),'data_analysis'),os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))),'modules')])
# print(sys.path)
from PIL import Image
import readconfig
import cus_data
import get_data
import functools
from datetime import datetime,date
import time
from dateutil.relativedelta import relativedelta
import re
import threading
from multiprocessing import Value
import xlwings as xw
import pandas as pd
pd.set_option('display.unicode.east_asian_width', True) #设置输出右对齐
# pd.set_option('display.max_columns', None) #显示所有列
from flask import Flask, request, jsonify,render_template,session,redirect,url_for,send_file
import pymysql
import datetime
from decimal import Decimal
import json
import random
import hashlib
import base64
from werkzeug.utils import secure_filename

def login_required(f):
    """登录验证装饰器"""
    @functools.wraps(f)
    def decorated_function(*args, **kwargs):
        if 'logged_in' not in session or not session['logged_in']:              
            return redirect(url_for('login'))
        print(f"{session.get('ins_id')} {session.get('user')}  正在操作{f.__name__} 页面。。。")
        return f(*args, **kwargs)
    return decorated_function

class LzService(Flask):

    def __init__(self,*args,**kwargs):
        super(LzService, self).__init__(*args, **kwargs)
        config_fn=os.path.join(os.path.join(os.path.dirname(__file__),'config','lz_service.config'))
        # self.config_lz=readconfig.exp_json2(config_fn)
        with open(config_fn,'r',encoding='utf-8') as f:
            self.config_lz=json.load(f)
        # print(self.config_lz)
        self.app_lock=threading.Lock()
        self.cus_id_prefix=self.config_lz['cus_id_prefix']
        

        #路由
        #渲染页面
        #检测页面
        self.add_url_rule('/login_check', view_func=self.login_check,methods=['GET','POST'])
        self.add_url_rule('/test_page',view_func=self.test_page,methods=['GET','POST'])



        #首页/登录页

        self.add_url_rule('/',view_func=self.login)
        self.add_url_rule('/login',view_func=self.login)
        #菜单页
        self.add_url_rule('/main',view_func=self.main)    
        #菜单页
        self.add_url_rule('/index',view_func=self.index)     
        #修改密码页
        self.add_url_rule('/reedit_pwd',view_func=self.reedit_pwd_page)      
        #获取客户信息页面
        self.add_url_rule('/cus_infos',view_func=self.cus_infos)
        #欢迎页面
        self.add_url_rule('/welcome',view_func=self.welcome)
        #欢迎页面
        self.add_url_rule('/test_copy',view_func=self.test_copy)
        #上课记录页面
        self.add_url_rule('/cus_cls_input',view_func=self.cus_cls_input)
        #新生成客户页面
        self.add_url_rule('/new_cus', view_func=self.new_cus,methods=['GET','POST'])
        #购课页面
        self.add_url_rule('/input_buy', view_func=self.input_buy,methods=['GET','POST'])
        #成功页面
        self.add_url_rule('/success', view_func=self.success,methods=['GET','POST'])
        #围度测量页面
        self.add_url_rule('/input_body', view_func=self.input_body,methods=['GET','POST'])
        #限时课开课页面
        self.add_url_rule('/start_limit_class', view_func=self.start_limit_class,methods=['GET','POST'])
        # 体验课上课页面
        self.add_url_rule('/trial_class', view_func=self.trial_class,methods=['GET','POST'])
        # 上课预约页面
        self.add_url_rule('/ins_book', view_func=self.ins_book_page,methods=['GET','POST'])
        # 显示客户报告的页面
        self.add_url_rule('/show_body_data', view_func=self.show_body_data,methods=['GET','POST'])
        # 显示查询经营记录
        self.add_url_rule('/show_business', view_func=self.show_business,methods=['GET','POST'])
        # 显示查询经营记录
        self.add_url_rule('/show_plan', view_func=self.show_plan,methods=['GET','POST'])
        # 显示训练项目编辑页面
        self.add_url_rule('/update_train_item', view_func=self.update_train_item_page,methods=['GET','POST'])
        # 热量计算页面
        self.add_url_rule('/cal_calory', view_func=self.cal_calory_page,methods=['GET','POST'])
        # 日期计算页面
        self.add_url_rule('/cal_enddate', view_func=self.cal_enddate_page,methods=['GET','POST'])
        # 客户信息查询页面
        self.add_url_rule('/show_cus_buyrec_lmtrec', view_func=self.show_cus_buyrec_lmtrec_page,methods=['GET','POST'])
        # 客户教练上课统计页面
        self.add_url_rule('/show_ins_cls_stat', view_func=self.show_ins_cls_stat_page,methods=['GET','POST'])
        # 客户既往训练项目页面
        self.add_url_rule('/show_cus_train_bydate', view_func=self.show_cus_train_bydate_page,methods=['GET','POST'])
        # 30天15节限时课程统计页面
        self.add_url_rule('/show_lmt15_info', view_func=self.show_lmt15_info_page,methods=['GET','POST'])
        # 客户肖像统计页面
        self.add_url_rule('/show_cus_img', view_func=self.show_cus_img_page,methods=['GET','POST'])
        # 客户信息查询页面 
        self.add_url_rule('/show_cus_info', view_func=self.show_cus_info_page,methods=['GET','POST']) 
        # 工作室总体上课情况
        self.add_url_rule('/show_all_cls', view_func=self.show_all_cls_page,methods=['GET','POST']) 
        #会员运动打卡页面
        self.add_url_rule('/show_cus_calendar', view_func=self.show_cus_calendar_page,methods=['GET','POST']) 
        #上传会员头像
        self.add_url_rule('/upload_cus_photo', view_func=self.upload_cus_photo_page,methods=['GET','POST']) 
        #本月到期限时课程清单页面
        self.add_url_rule('/show_this_due_cus_list', view_func=self.show_this_due_cus_list_page,methods=['GET','POST']) 
        #会员生日清单页面
        self.add_url_rule('/show_cus_birthday_list', view_func=self.show_cus_birthday_list_page,methods=['GET','POST']) 
        #查询优惠卡界面
        self.add_url_rule('/show_vc', view_func=self.show_vc_page,methods=['GET','POST']) 
        #设置优惠卡界面
        self.add_url_rule('/set_vc', view_func=self.set_vc_page,methods=['GET','POST']) 
        #返回优惠卡颜色列表
        self.add_url_rule('/get_vc_cfg', view_func=self.get_vc_cfg,methods=['GET','POST']) 
        #所有到期续课提醒页面
        self.add_url_rule('/show_all_after_due_cus', view_func=self.show_all_after_due_cus_page,methods=['GET','POST']) 
        #修改过期失效卡片结束日
        self.add_url_rule('/edit_pass_due_card', view_func=self.edit_pass_due_card,methods=['GET','POST']) 
        #连续购课上课超12次页面
        self.add_url_rule('/show_continue_buy_list', view_func=self.show_continue_buy_list,methods=['GET','POST']) 
        #转换课程类型页面
        self.add_url_rule('/transfer_cls_type', view_func=self.transfer_cls_type,methods=['GET','POST']) 
        #显示每日上课情况页面
        self.add_url_rule('/show_day_cls_stat', view_func=self.show_day_cls_stat,methods=['GET','POST']) 
        #显示每9.9元到期页面
        self.add_url_rule('/show_9point9_due_page', view_func=self.show_9point9_due_page,methods=['GET','POST']) 
        #显示团课录入页面
        self.add_url_rule('/group_class_input', view_func=self.group_class_input_page,methods=['GET','POST'])  
        #显示薪酬统计录入页面
        self.add_url_rule('/ins_salary', view_func=self.ins_salary_page,methods=['GET','POST'])  
        #显示薪酬报告页面
        self.add_url_rule('/get_ins_salary_report_page', view_func=self.get_ins_salary_report_page,methods=['GET','POST'])         
        #显示后面薪酬管理员页面
        self.add_url_rule('/admin', view_func=self.salary_index_page,methods=['GET','POST'])       
        #显示查询购课记录
        self.add_url_rule('/get_buy_stat', view_func=self.get_buy_stat_page,methods=['GET','POST'])      

        #功能
        #登出处理
        self.add_url_rule('/logout',view_func=self.logout)
        #修改密码处理
        self.add_url_rule('/deal_reedit_pwd',view_func=self.deal_reedit_pwd_db,methods=['GET','POST'])
        #从模板.xlsm获取基本信息，如教练姓名、课程种类等
        self.add_url_rule('/get_template_info', view_func=self.get_template_info_db,methods=['GET','POST'])
        #获取教练昵称
        self.add_url_rule('/get_ins_nickname', view_func=self.get_ins_list_nickname_db,methods=['GET','POST'])        
        #获取数据类型
        self.add_url_rule('/check_database_type', view_func=self.check_database_type,methods=['GET','POST'])        

        #获取页面文本
        self.add_url_rule('/get_page_txts', view_func=self.get_page_txts,methods=['GET','POST'])     
        #获取训练计划页面文本
        self.add_url_rule('/get_plan_page_txts', view_func=self.get_plan_page_txts,methods=['GET','POST'])             
        #获取训练计划日期列表
        self.add_url_rule('/get_train_plan_dates', view_func=self.get_train_plan_dates,methods=['GET','POST'])               
        #获取训练计划日期列表
        self.add_url_rule('/get_train_plan', view_func=self.get_train_plan,methods=['GET','POST'])               
        #遍历会员资料文件夹获取所有 客户列表
        self.add_url_rule('/get_cus_list', view_func=self.get_cus_list_db,methods=['GET','POST'])
        #从,'03-教练管理','教练资料','教练信息.xlsx'中获取教练列表
        self.add_url_rule('/get_ins_list', view_func=self.get_ins_list_db,methods=['GET','POST'])
        self.add_url_rule('/get_all_ins_list', view_func=self.get_all_ins_list_db,methods=['GET','POST'])
        #获取客户信息，包括既往购课、上课信息，剩余课时信息，限时课程信息，围度测量信息
        self.add_url_rule('/get_cus_info', view_func=self.get_cus_info_db,methods=['GET','POST'])
        #获取客户未开课的购课编码
        self.add_url_rule('/get_cus_cls_infos', view_func=self.get_cus_cls_infos_db,methods=['GET','POST'])
        #通过'05-专业资料','训练项目.xlsx'获取训练项目的名称及分类
        self.add_url_rule('/get_train_list', view_func=self.get_train_list_db,methods=['GET','POST'])    
        # 获取体验课记录
        self.add_url_rule('/get_trial_list', view_func=self.get_trial_list_db,methods=['GET','POST']) 
        #获取客户既往购课记录,并整理合并
        # 获取有效的卡
        self.add_url_rule('/get_cards', view_func=self.get_cards,methods=['GET','POST']) 
        #获取客户既往购课记录,并整理合并
        #按不同的类型获取有效的卡号返回给购卡页面
        self.add_url_rule('/get_cus_cards_by_type', view_func=self.get_cus_cards_by_type,methods=['GET','POST'])
        #获取限时卡记录返回给限时课程开课页面
        self.add_url_rule('/send_to_start_lmt_page_data',view_func=self.send_to_start_lmt_page_data,methods=['GET','POST'])
        self.add_url_rule('/get_cus_buy', view_func=self.get_cus_buy_db,methods=['GET','POST'])
        #获取客户的限时卡返回修改过期卡号页面edit_pass_due_card.html
        self.add_url_rule('/get_pass_due_rec',view_func=self.get_pass_due_rec,methods=['GET','POST'])
        #获取既往体测记录
        self.add_url_rule('/get_body_history', view_func=self.get_body_history_db,methods=['GET','POST'])
        #获取行业及职业
        self.add_url_rule('/get_industry_occu', view_func=self.get_industry_occu,methods=['GET','POST'])
        #获取上5节课上课简略记录
        self.add_url_rule('/get_train_summary', view_func=self.get_train_summary,methods=['GET','POST'])
        #获取客户阶段报告-身体
        self.add_url_rule('/get_cus_report_body',view_func=self.get_cus_report_body,methods=['GET','POST'])
        #获取客户阶段报告-训练
        self.add_url_rule('/get_cus_report_train',view_func=self.get_cus_report_train,methods=['GET','POST'])
        #获取客户对比图
        self.add_url_rule('/get_compare_image',view_func=self.get_compare_image,methods=['GET','POST'])
        #写入更新的训练项目
        self.add_url_rule('/deal_update_train_item',view_func=self.deal_update_train_item,methods=['GET','POST'])
        #获取热量计算参数
        self.add_url_rule('/deal_calculate_calory',view_func=self.deal_calculate_calory,methods=['GET','POST'])
        #处理查询客户购课页面
        self.add_url_rule('/deal_show_cus_buyrec_lmtrec', view_func=self.show_cus_buyrec_lmtrec,methods=['GET','POST'])
        #处理index页面的会员训练情况请求
        self.add_url_rule('/get_latest_cus_train_days', view_func=self.get_latest_cus_train_days,methods=['GET','POST'])
        #处理按天查询会员训练项目的请求
        self.add_url_rule('/query_cus_train_item_bydate', view_func=self.deal_query_cus_train_item_bydate,methods=['GET','POST'])
        #处理限时课程30天15节信息请求
        self.add_url_rule('/query_lmt_15', view_func=self.deal_query_lmt_15,methods=['GET','POST'])
        #处理教练上课节数频数信息的请求
        self.add_url_rule('/get_ins_cls_info', view_func=self.deal_get_ins_cls_info,methods=['GET','POST'])
        #处理会员基本信息请求
        self.add_url_rule('/get_cus_basic_info', view_func=self.deal_get_cus_basic_info,methods=['GET','POST'])
        #处理会员信息统计的请求
        self.add_url_rule('/get_all_cus_stat', view_func=self.deal_get_all_cus_stat,methods=['GET','POST'])
        #获取客户头像
        self.add_url_rule('/get_cus_image', view_func=self.get_cus_image,methods=['GET','POST'])
        #获取店面总体上课情况
        self.add_url_rule('/get_all_cls_info', view_func=self.deal_get_all_cls_info,methods=['GET','POST'])
        #会员运动打卡日历信息
        self.add_url_rule('/get_train_calendar', view_func=self.deal_get_train_calendar,methods=['GET','POST']) 
        #营收日历信息 
        self.add_url_rule('/get_income_calendar_data', view_func=self.deal_get_income_calendar_data,methods=['GET','POST']) 
        #教练当月业绩 
        self.add_url_rule('/get_ins_job_stat', view_func=self.get_ins_job_stat,methods=['GET','POST']) 
        #当月到期限时会员记录
        self.add_url_rule('/get_this_month_due_data', view_func=self.get_this_month_due_data,methods=['GET','POST']) 
        #获取会员生日清单
        self.add_url_rule('/get_cus_birthday', view_func=self.get_cus_birthday,methods=['GET','POST']) 
        #获取会员优惠券清单
        self.add_url_rule('/get_cus_vc_list',view_func=self.get_cus_vc_list,methods=['GET','POST'])
        #获取工作室按收入类型的分类汇总数据
        self.add_url_rule('/get_income_stat_by_type',view_func=self.get_income_stat_by_type,methods=['GET','POST'])
        #获取会员实付金额贡献度数据
        self.add_url_rule('/get_cus_contribute_data',view_func=self.get_cus_contribute_data,methods=['GET','POST'])
        #获取按月营销数据
        self.add_url_rule('/get_business_data_by_month',view_func=self.get_business_data_by_month,methods=['GET','POST'])
        #获取课程结束或到期会员
        self.add_url_rule('/get_after_duedate_data',view_func=self.get_after_duedate_data,methods=['GET','POST'])
        #获取课程结束或到期会员
        self.add_url_rule('/get_trial_problem',view_func=self.get_trial_problem,methods=['GET','POST']) 
        #获取会员上课时间段统计
        self.add_url_rule('/get_cus_cls_hour_qty',view_func=self.get_cus_cls_hour_qty,methods=['GET','POST']) 
        #处理过期卡片的新结束日
        self.add_url_rule('/edit_pass_due_card_new_end_date',view_func=self.edit_pass_due_card_new_end_date,methods=['GET','POST']) 
        #获取9.9元课程信息
        self.add_url_rule('/get_9_point_9_yuan_cls',view_func=self.get_9_point_9_yuan_cls,methods=['GET','POST']) 
        #获取连续续课返现课程清单
        self.add_url_rule('/get_continue_buy_cls_rec',view_func=self.get_continue_buy_cls_rec,methods=['GET','POST']) 
        #获取所有连续续课返现课程清单
        self.add_url_rule('/get_all_continue_buy_cls_rec',view_func=self.get_all_continue_buy_cls_rec,methods=['GET','POST']) 
        #转换课程类型页面返回有效课程
        self.add_url_rule('/get_valid_cards_list', view_func=self.get_valid_cards,methods=['GET','POST']) 
        #处理转换课程
        self.add_url_rule('/deal_transfer_lmt_to_dms', view_func=self.deal_transfer_lmt_to_dms,methods=['GET','POST']) 
        #获取日上课课程统计数据
        self.add_url_rule('/get_day_cls_data', view_func=self.get_day_cls_data,methods=['GET','POST']) 
        #获取到期的9.9元课程数据
        self.add_url_rule('/get_due_9point9_data', view_func=self.get_due_9point9_data,methods=['GET','POST']) 
        #客户连续购课查询
        self.add_url_rule('/get_cus_continue_buy', view_func=self.get_cus_continue_buy,methods=['GET','POST']) 
        #活动赠课查询
        self.add_url_rule('/get_activity_cls_info', view_func=self.get_activity_cls_info,methods=['GET','POST']) 
        #读取会员等级
        self.add_url_rule('/get_cus_stage_config', view_func=self.get_cus_stage_config,methods=['GET','POST']) 
        

        #打开客户的xlsm文件
        self.add_url_rule('/open_cus_fn', view_func=self.open_cus_fn,methods=['GET','POST'])
        #遍历会员资料文件夹，生成新的客户ID号
        # self.add_url_rule('/check_new', view_func=self.check_new,methods=['GET','POST'])
        #获取体测录入页面中的文本
        self.add_url_rule('/get_input_body_page_options', view_func=self.get_input_body_page_options,methods=['GET','POST'])
        

        #生成新会员
        self.add_url_rule('/generate_new', view_func=self.generate_new_db,methods=['GET','POST'])
        #写入购课记录
        self.add_url_rule('/write_buy', view_func=self.write_buy_db,methods=['GET','POST'])
        
        #获取客户既往购课记录表，不整理
        self.add_url_rule('/get_cus_buy_list', view_func=self.get_cus_buy_list,methods=['GET','POST'])        
           
        #执行写入上课记录、写入训练记录
        self.add_url_rule('/deal_cls', view_func=self.deal_cls_db,methods=['GET','POST'])
        
        #写入体测记录
        self.add_url_rule('/write_body', view_func=self.write_body_db,methods=['GET','POST'])
        #处理开课记录
        self.add_url_rule('/deal_start_class_page', view_func=self.deal_start_class_page_db,methods=['GET','POST'])
        
        # 写入体验课上课记录
        self.add_url_rule('/write_trial_rec', view_func=self.write_trial_rec_db,methods=['GET','POST'])
        #查询约课记录
        self.add_url_rule('/get_book_data',view_func=self.get_book_data_db,methods=['GET','POST'])
        #写入约课记录表
        self.add_url_rule('/write_ins_book',view_func=self.write_ins_book_db,methods=['GET','POST'])
        #处理login
        self.add_url_rule('/deal_login',view_func=self.deal_login,methods=['GET','POST'])
        #检查login状态
        self.add_url_rule('/check_login',view_func=self.check_login,methods=['GET','POST'])
        #返回小程序首页显示教练的控制
        self.add_url_rule('/show_who_ins',view_func=self.show_who_ins,methods=['GET','POST'])
        #返回微信小程序约课页面下方广告图片
        self.add_url_rule('/get_wxprg_ads_pic',view_func=self.get_wxprg_ads_pic,methods=['GET','POST'])
        self.add_url_rule('/send_ads_pic',view_func=self.send_ads_pic,methods=['GET','POST'])
        #查询运营数据
        self.add_url_rule('/get_business_data',view_func=self.get_business_data,methods=['GET','POST'])
        #处理上传图片
        self.add_url_rule('/upload_pic',view_func=self.upload_pic,methods=['GET','POST'])
        #检查是否有过既往的训练记录
        self.add_url_rule('/get_plan_history',view_func=self.get_plan_history,methods=['GET','POST'])
        #处理会员照片上传
        self.add_url_rule('/deal_upload_cus_photo', view_func=self.upload_cus_photo,methods=['GET','POST'])      
        #写入优惠券使用数据表
        self.add_url_rule('/write_vc_use', view_func=self.write_vc_use,methods=['GET','POST'])   
        #生成优惠券
        self.add_url_rule('/write_new_vc', view_func=self.write_new_vc,methods=['GET','POST'])  
        #返回每月新增会员数量
        self.add_url_rule('/new_cus_count_by_month', view_func=self.new_cus_count_by_month,methods=['GET','POST']) 



        #返回有效的小团课清单
        self.add_url_rule('/get_group_cus_list', view_func=self.get_group_cus_list,methods=['GET','POST'])  
        #写入团课小清单的上课内容等
        self.add_url_rule('/deal_group_class',view_func=self.deal_group_class,methods=['GET','POST'])
        #获取薪酬基础参数
        self.add_url_rule('/get_salary_params',view_func=self.get_salary_params,methods=['GET','POST'])
        #新增写入薪酬结算表 
        self.add_url_rule('/write_cal_ins_salary',view_func=self.write_cal_ins_salary,methods=['GET','POST'])
        #修改写入薪酬结算表结算标记 
        # self.add_url_rule('/update_cal_ins_salary',view_func=self.update_cal_ins_salary,methods=['GET','POST'])
        #根据前端传来的月份获取相应的薪酬表
        self.add_url_rule('/get_ins_salary',view_func=self.get_ins_salary,methods=['GET','POST'])


        #保存salary report数据
        self.add_url_rule('/save_salary_report',view_func=self.save_salary_report,methods=['GET','POST'])
        #查询salary report 
        self.add_url_rule('/get_ins_salary_report',view_func=self.get_ins_salary_report,methods=['GET','POST'])

        #查询购课清单
        self.add_url_rule('/get_buy_list',view_func=self.get_buy_list,methods=['GET','POST'])

        #查询会员购课总金额
        self.add_url_rule('/get_cus_buy_sum',view_func=self.get_cus_buy_sum,methods=['GET','POST'])
        
        #查询会员购课总金额
        self.add_url_rule('/show_cus_reward_points',view_func=self.show_cus_reward_points_page,methods=['GET','POST'])
        self.add_url_rule('/get_cus_rewards',view_func=self.get_cus_rewards,methods=['GET','POST'])
        

        
        

    def connect_mysql(self):
        with open(os.path.join(os.path.dirname(os.path.realpath((__file__))),'config','db.config'),'r',encoding='utf-8') as f:
            cfg=json.load(f)
        # 连接数据库
        conn = pymysql.connect(
            host=cfg['host'],       # 数据库主机地址
            user=cfg['user'],     # 数据库用户名
            password=cfg['password'], # 数据库密码
            database=cfg['database'],  # 要连接的数据库名称
            port=cfg['port']
        )

        return conn
    
    @login_required
    def show_cus_reward_points_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_cus_reward_points.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)
    
    def get_cus_rewards(self):
        cus_id=request.args.get('cus_id')
        try:
            sql='''
            select brt.cus_id,brt.cus_name,cit.birthday,cit.sex,sum(brt.pay) as total_pay, sum(brt.real_pay) as total_real_pay , round(sum(brt.real_pay)/10) as reward_point from buy_rec_table brt
            left join cus_info_table cit on cit.cus_id=brt.cus_id
            where brt.cus_id=%s group by brt.cus_id, brt.cus_name;
            '''
            conn=self.connect_mysql()
            cursor=conn.cursor()
            cursor.execute(sql,(cus_id,))

            res=cursor.fetchall()
            if res:
                res_dic=self.res_to_dic(res,('cus_id','cus_name','birthday','sex','total_pay','total_real_pay','reward_points'))
                return jsonify({'res':'ok','msg':'success','data':res_dic})
            else:
                return jsonify({'res':'failed','msg':f'no cus reward points'})

        except Exception as e:
            print('get cus reward points error:',str(e))
            return jsonify({'res':'failed','msg':f'get cus reward points error,{e}'})
            


    def get_cus_buy_sum(self):
        
        # data = request.json
        # if 'cus_id_name' not in data or len(data['cus_id_name']) < 7:
        #     return jsonify({'res': 'failed', 'msg': 'Invalid customer ID'})
        # cus_id = data['cus_id_name'][:7]

        cus_id=request.args.get('cus_id')
        print('get_cus_buy_sum()获取的cus_id:',cus_id)
        
        conn = self.connect_mysql()
        cursor = conn.cursor()

        try:
            sql = '''
            select cus_id, cus_name, sum(real_pay) as total_amount 
            from buy_rec_table 
            where cus_id=%s 
            group by cus_id, cus_name
            '''
            cursor.execute(sql, (cus_id,))
            res = cursor.fetchall()

            if not res:
                return jsonify({'res': 'failed', 'msg': 'Customer not found or no purchase records'})
                
            res_dic = self.res_to_dic(res, ('cus_id', 'cus_name', 'total_amount'))
            return jsonify({'res': 'ok', 'msg': 'success', 'data': res_dic})

        except Exception as e:
            print('get cus buy sum error:', str(e))
            return jsonify({'res': 'failed', 'msg': str(e)})
        finally:
            cursor.close()
            conn.close()


    def get_cus_stage_config(self):
        with open(r'./config/cus_stage_config.json','r',encoding='utf-8') as f:
            config = json.load(f)

        return jsonify({'stage_config':config})

    def get_buy_list(self):
        data = request.json
        print('getting buy list data...')
        print('received dates:',data)

        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            sql='''
            select brt.cus_id,brt.cus_name,brt.buy_date,brt.card_id,brt.buy_flow_id,brt.pay,brt.real_pay,ct.cashier_name,brt.comment,brt.adm_cmt from buy_rec_table brt 
            left join cashier_table ct on ct.cashier_id=brt.cashier_id
            where brt.buy_date BETWEEN %s and %s
            order by buy_date desc;
            '''

            cursor.execute(sql,(data['start_date'],data['end_date']))
            res=cursor.fetchall()

            sql='''
            SELECT 
                buy_date,
               	round(sum(real_pay)*0.9965,2) as real_real_pay,
                GROUP_CONCAT(
                    CONCAT(cus_id, cus_name, ' ', buy_flow_id) 
                    SEPARATOR ';'
                ) AS cus_id_name_buy_flow_id,
                sum(pay) as pay,
               sum(real_pay) as real_pay
            FROM 
                buy_rec_table
            where buy_date between %s and %s
            GROUP BY 
                buy_date
            ORDER BY 
                buy_date DESC;
            '''
            cursor.execute(sql,(data['start_date'],data['end_date']))
            res_combine=cursor.fetchall()            

            if res and res_combine:
                cols=('cus_id','cus_name','buy_date','card_id','buy_flow_id','pay','real_pay','cashier_name','comment','adm_cmt')
                cols_combine=('buy_date','real_real_pay','cus_id_name_buy_flow_id','pay','real_pay')
                
                raw_res=self.res_to_dic(res,cols)
                res_combine_out=self.res_to_dic(res_combine,cols_combine)
                return jsonify({'res':'ok','raw_data':raw_res,'combine_data':res_combine_out})
            else:
                print('no buy list data')
                return jsonify({
                    'res': 'failed',
                    'msg': 'no buy list data'
                })

        except Exception as e:
            print('get buy list error:', e)
            return jsonify({
                'res': 'failed',
                'msg': str(e)
            })

        finally:
            cursor.close()
            conn.close()

    
    def get_ins_salary_report(self):
        data = request.json
        print('getting salary report data...')
        # print('Received get_ins_salary_report data:', data)
       
        conn = self.connect_mysql()
        cursor = conn.cursor()

        try:
            month = data['month'] + '-01'  # 格式化为YYYY-MM-01
            ins_id = data['ins_id']
            print(f'Querying saved report for ins_id: {ins_id}, month: {month}')

            # 1. 查询报告基本信息
            sql_report = '''
                SELECT 
                    id, ins_name, basic_salary, basic_comment,
                    private_class_total, group_class_total, sales_total, sales_range,other_remarks,other_amount,total
                FROM salary_save_reports
                WHERE ins_id = %s AND month = %s
            '''
            cursor.execute(sql_report, (ins_id, month))
            report = cursor.fetchone()

            if not report:
                return jsonify({
                    'res': 'failed',
                    'error': '未找到该月份的薪酬报告'
                })

            report_id = report[0]
            result = {
                'ins_name': report[1],
                'basic_salary': float(report[2]),
                'basic_comment': report[3],
                'private_class_total': float(report[4]),
                'group_class_total': float(report[5]),
                'sales_total': float(report[6]),
                'sales_range': report[7],
                'other_remarks':report[8],
                'other_amount':report[9],
                'total': float(report[10]),
                'month': data['month'],
                'ins_id': ins_id
            }

            # 2. 查询私教课详情
            sql_private = '''
                SELECT 
                    card_id, cls_type, cls_name, pay,real_pay,per_class_price,cus_name,
                    cls_count, buy_date, commission,remark
                FROM salary_save_private_details
                WHERE report_id = %s
                ORDER BY buy_date
            '''
            cursor.execute(sql_private, (report_id,))
            private_details = []
            for row in cursor.fetchall():
                private_details.append({
                    'card_id': row[0],
                    'class_type': row[1],
                    'class_name': row[2],
                    'pay':float(row[3]),
                    'real_pay':float(row[4]),
                    'per_class_price':float(row[5]),
                    'cus_name': row[6],
                    'class_count': row[7],
                    'buy_date': row[8].strftime('%Y-%m-%d') if row[6] else None,
                    'commission': float(row[9]),
                    'remark':row[10]
                })
            result['private_class_details'] = private_details

            # 3. 查询小团课详情
            sql_group = '''
                SELECT 
                    member, cls_type, cls_name,
                    cls_time, cls_count, commission
                FROM salary_save_group_details
                WHERE report_id = %s
                ORDER BY cls_time
            '''
            cursor.execute(sql_group, (report_id,))
            group_details = []
            for row in cursor.fetchall():
                group_details.append({
                    'member': row[0],
                    'class_type': row[1],
                    'class_name': row[2],
                    'cls_time': row[3].strftime('%Y-%m-%d') if row[3] else None,
                    'cls_count': row[4],
                    'commission': float(row[5])
                })
            result['group_class_details'] = group_details

            # 4. 查询销售详情
            sql_sales = '''
                SELECT 
                    cus_name, buy_date, card_id,
                    pay_amount, real_pay, comment, commission
                FROM salary_save_sales_details
                WHERE report_id = %s
                ORDER BY buy_date
            '''
            cursor.execute(sql_sales, (report_id,))
            sales_details = []
            for row in cursor.fetchall():
                sales_details.append({
                    'cus_name': row[0],
                    'buy_date': row[1].strftime('%Y-%m-%d') if row[1] else None,
                    'card_id': row[2],
                    'pay_amount': float(row[3]),
                    'real_pay': float(row[4]),
                    'comment': row[5],
                    'commission': float(row[6])
                })
            result['sales_details'] = sales_details

            # 5. 查询未结算课程详情
            sql_unsettled = '''
                SELECT 
                    card_id, cls_type, cls_name,
                    cus_name, buy_date, start_date, days_since_start
                FROM salary_save_unsettled_details
                WHERE report_id = %s
                ORDER BY buy_date
            '''
            cursor.execute(sql_unsettled, (report_id,))
            unsettled_details = []
            for row in cursor.fetchall():
                unsettled_details.append({
                    'card_id': row[0],
                    'cls_type': row[1],
                    'cls_name': row[2],
                    'cus_name': row[3],
                    'buy_date': row[4].strftime('%Y-%m-%d') if row[4] else None,
                    'start_date': row[5].strftime('%Y-%m-%d') if row[5] else '未开课',
                    'days_since_start': row[6]
                })
            result['unsettled_details'] = unsettled_details

            return jsonify({
                'res': 'ok',
                'data': result
            })

        except Exception as e:
            print('get_ins_salary_report error:', e)
            return jsonify({
                'res': 'failed',
                'error': str(e)
            })

        finally:
            cursor.close()
            conn.close()
    
    def save_salary_report(self):
        data = request.json
        print('saving salary report...')
        print('received save salary data:', data)
        conn = self.connect_mysql()
        cursor = conn.cursor()

        try:
            # 开始事务
            conn.begin()
            
            # 更新薪酬结算标记
            update_result = self.update_cal_ins_salary_internal(cursor, data)
            
            # 准备月份数据(格式化为YYYY-MM-01)
            month = data['month'] + '-01'
            ins_id = data['insId']
            
            # 1. 检查主表记录是否存在
            cursor.execute(
                "SELECT id FROM salary_save_reports WHERE ins_id = %s AND month = %s",
                (ins_id, month)
            )
            report = cursor.fetchone()
            
            if report:
                # 更新主表记录
                report_id = report[0]
                cursor.execute('''
                    UPDATE salary_save_reports SET
                        ins_name = %s,
                        basic_salary = %s,
                        basic_comment = %s,
                        private_class_total = %s,
                        group_class_total = %s,
                        sales_total = %s,
                        sales_range = %s,
                        other_remarks= %s,
                        other_amount = %s,
                        total = %s
                    WHERE id = %s
                ''', (
                    data['insName'],
                    data['basicSalary'],
                    data['basicComment'],
                    data['privateClassTotal'],
                    data['groupClassTotal'],
                    data['salesTotal'],
                    data['salesRange'],
                    data['otherRemarks'],
                    data['otherAmount'],
                    data['total'],
                    report_id
                ))
                
                # 删除旧的详情记录
                cursor.execute("DELETE FROM salary_save_private_details WHERE report_id = %s", (report_id,))
                cursor.execute("DELETE FROM salary_save_group_details WHERE report_id = %s", (report_id,))
                cursor.execute("DELETE FROM salary_save_sales_details WHERE report_id = %s", (report_id,))
                cursor.execute("DELETE FROM salary_save_unsettled_details WHERE report_id = %s", (report_id,))  # 新增：删除未结算记录
            else:
                # 插入新主表记录
                cursor.execute('''
                    INSERT INTO salary_save_reports (
                        ins_id, ins_name, month, basic_salary, basic_comment,
                        private_class_total, group_class_total, sales_total, sales_range,other_remarks,other_amount,total
                    ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s,%s,%s,%s)
                ''', (
                    ins_id,
                    data['insName'],
                    month,
                    data['basicSalary'],
                    data['basicComment'],
                    data['privateClassTotal'],
                    data['groupClassTotal'],
                    data['salesTotal'],
                    data['salesRange'],
                    data['otherRemarks'],
                    data['otherAmount'],
                    data['total']
                ))
                report_id = cursor.lastrowid
            
            # 2. 插入私教课详情
            for item in data.get('privateClassDetails', []):
                cursor.execute('''
                    INSERT INTO salary_save_private_details (
                        report_id, card_id, cls_type, cls_name, pay,real_pay,per_class_price,cus_name,
                        cls_count, buy_date, commission,remark
                    ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s,%s,%s,%s)
                ''', (
                    report_id,
                    item['cardId'],
                    item['clsType'],
                    item['clsName'],
                    item['pay'],
                    item['real_pay'],
                    item['perClassPrice'],
                    item['cusName'],
                    item['clsCount'],
                    item['buyDate'],
                    float(item['commission'].strip()),
                    item['remark'].strip()
                ))
            
            # 3. 插入小团课详情
            for item in data.get('groupClassDetails', []):
                cursor.execute('''
                    INSERT INTO salary_save_group_details (
                        report_id, member, cls_type, cls_name,
                        cls_time, cls_count, commission
                    ) VALUES (%s, %s, %s, %s, %s, %s, %s)
                ''', (
                    report_id,
                    item['member'],
                    item['clsType'],
                    item['clsName'],
                    item['clsTime'],
                    item['clsCount'],
                    float(item['commission'])
                ))
            
            # 4. 插入销售详情
            for item in data.get('salesDetails', []):
                cursor.execute('''
                    INSERT INTO salary_save_sales_details (
                        report_id, cus_name, buy_date, card_id,
                        pay_amount, real_pay, comment, commission
                    ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
                ''', (
                    report_id,
                    item['cusName'],
                    item['buyDate'],
                    item['cardId'],
                    float(item['payAmount']),
                    float(item['realPay']),
                    item['comment'],
                    float(item['commission'])
                ))
            
            # 5. 新增：插入未结算课程详情
            for item in data.get('unsettledDetails', []):
                cursor.execute('''
                    INSERT INTO salary_save_unsettled_details (
                        report_id, card_id, cls_type, cls_name,
                        cus_name, buy_date, start_date, days_since_start
                    ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
                ''', (
                    report_id,
                    item['card_id'],
                    item['cls_type'],
                    item['cls_name'],
                    item['cus_name'],
                    item['buy_date'],
                    item['start_date'] if item['start_date'] != 'None' else None,
                    item['days_since_start']
                ))
            
            # 提交事务
            conn.commit()
            return jsonify({
                'res': 'ok', 
                'msg': '报告保存成功',
                'update_result': update_result
            })
            
        except Exception as e:
            conn.rollback()
            print('保存报告失败:', e)
            return jsonify({'res': 'failed', 'msg': str(e)})
        
        finally:
            cursor.close()
            conn.close()
  

    def update_cal_ins_salary_internal(self, cursor, data):
        """供save_salary_report内部调用的更新方法"""
        success_count = 0
        failed_records = []
        
        try:
            # 处理结算课程
            if 'privateClassDetails' in data:
                for item in data['privateClassDetails']:
                    try:
                        cursor.execute('''
                        UPDATE card_salary_stat 
                        SET cal_salary = 'yes',
                            operator_id = %s,
                            operate_time = %s
                        WHERE card_id = %s
                        ''', (data['insId'], datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), item['cardId']))
                        success_count += 1
                    except Exception as e:
                        failed_records.append({
                            'card_id': item.get('cardId', 'N/A'),
                            'error': str(e)
                        })
                        print(f'Update failed for card_id {item.get("cardId")}: {e}')

            # 处理未结算课程
            if 'unsettledDetails' in data:
                for item in data['unsettledDetails']:
                    try:
                        cursor.execute('''
                        UPDATE card_salary_stat 
                        SET cal_salary = 'no',
                            operator_id = %s,
                            operate_time = %s
                        WHERE card_id = %s
                        ''', (data['insId'], datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), item['card_id']))
                        success_count += 1
                    except Exception as e:
                        failed_records.append({
                            'card_id': item.get('card_id', 'N/A'),
                            'error': str(e)
                        })
                        print(f'Update failed for card_id {item.get("card_id")}: {e}')
            
            return {
                'updated_count': success_count,
                'failed_count': len(failed_records),
                'failed_records': failed_records
            }
                
        except Exception as e:
            raise Exception(f'更新薪酬结算标记失败: {str(e)}')
    
    @login_required
    def get_ins_salary_report_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./get_ins_salary_report.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)



    def get_salary_params(self):
        with open(os.path.join(os.path.dirname((__file__)),'config','salary_config'),'r',encoding='utf-8') as f:
            cfg=json.load(f)

        return jsonify(cfg)


    def deal_group_class(self):
        data=request.json
        print(data)
        cuses=data['members']
        train_items=data['trainings']
        ins=data['coach']
        cls_datetime=data['trainingDateTime']
        opr_id=data['opr_id']
        cls_cmt=data['classComment']
        calories=data['caloriesBurned']

        # {'memberIds': ['LZ00101'],
        #   'trainings': [{'name': '小团课训练', 'category': '有氧训练', 'muscle': '复合肌群', 'equipment': '综合'}], 
        #   'coachId': 'LZINS999', 
        #   'trainingDateTime': '2025-05-28T19:29'}

        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            opr_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            for cus in cuses:
                sql_cls_tkn='''
                    insert into cls_tkn_rec_table
                    (cus_id,cus_name,cls_datetime,cls_long,cls_type,cls_name,card_id,ins_id,ins_name,comment,operator_id,operate_time) 
                    values 
                    (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
                '''
                values=(cus['id'],cus['name'],cls_datetime,1,'限时课程','小团课',cus['card_id'],ins['id'],ins['name'],cls_cmt,opr_id,opr_time)
                cursor.execute(sql_cls_tkn,values)

                values_oxy=[]
                for train in train_items:
                    print('train data:',train)
                    #有氧训练写入
                    oxy_train_sql='''
                        insert into train_oxy_rec_table 
                        (train_datetime,cus_id,cus_name,oxy_name,oxy_time,oxy_group,calories,card_id,ins_id,ins_name,comment) 
                        values 
                        (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s);
                    '''
                    # print(cls_datetime,cus['id'],cus['name'],train['name'],60,1,calories,cus['card_id'],ins['id'],ins['name'],cls_cmt)
                    values_oxy.append((cls_datetime,cus['id'],cus['name'],train['name'],60,1,calories,cus['card_id'],ins['id'],ins['name'],cls_cmt))
                
                cursor.executemany(oxy_train_sql,values_oxy)

            conn.commit()
            print('小团课录入成功')
            return jsonify({'res':'ok'})
        except Exception as e:
            return jsonify({'res':'failed','error':str(e)})
        finally:
            cursor.close()
            conn.close()

    def get_ins_salary(self):
        data=request.json
        print('get ins salary data Received data:',data)
        conn = self.connect_mysql()
        cursor = conn.cursor()
        ins_id=data['ins_id']
        year_month=data['month']

        #私教课查询
        try:
            sql='''
                SELECT DISTINCT 
                    ctrt.card_id,
                    ctrt.cls_type,
                    ctrt.cls_name,
                    ctrt.cus_id,
                    ctrt.cus_name,
                    ctrt.ins_id,
                    ctrt.ins_name,
                    sum(cls_long) AS cls_tkn_qty,
                    ct.card_start_time AS card_start_time,
                    DATEDIFF(LAST_DAY(STR_TO_DATE(CONCAT(%s, '-01'), '%%Y-%%m-%%d')), ct.card_start_time)+1 AS days_from_start,
                    brt.pay,
                    brt.real_pay,
                    brt.pay/brt.buy_num AS avr_pay,
                    brt.buy_date,
                    css.cal_salary 
                FROM cls_tkn_rec_table ctrt 
                LEFT JOIN(
                    SELECT DISTINCT card_id, card_start_time 
                    FROM cards_table ct0 
                    WHERE card_start_time IS NOT NULL
                ) AS ct ON ct.card_id = ctrt.card_id
                LEFT JOIN buy_rec_table brt ON brt.card_id = ctrt.card_id
                LEFT JOIN card_salary_stat css ON css.card_id = ctrt.card_id
                WHERE 
                    ctrt.ins_id = %s 
                    AND ctrt.cls_datetime BETWEEN STR_TO_DATE(CONCAT(%s, '-01'), '%%Y-%%m-%%d') AND LAST_DAY(STR_TO_DATE(CONCAT(%s, '-01'), '%%Y-%%m-%%d'))
                    AND ctrt.cls_type = '限时课程' AND ctrt.cls_name <> '小团课'
                GROUP BY 
                    ctrt.card_id, ctrt.cus_id, ctrt.cus_name, ctrt.ins_id, ctrt.ins_name,
                    ct.card_start_time, brt.pay, brt.real_pay, ctrt.cls_type, ctrt.cls_name,
                    brt.buy_date, brt.buy_num, css.cal_salary
                HAVING days_from_start >= 10

                UNION

                SELECT DISTINCT 
                    ctrt.card_id,
                    ctrt.cls_type,
                    ctrt.cls_name,
                    ctrt.cus_id,
                    ctrt.cus_name,
                    ctrt.ins_id,
                    ctrt.ins_name,
                    sum(cls_long) AS cls_tkn_qty,
                    ct.card_start_time AS card_start_time,
                    DATEDIFF(LAST_DAY(STR_TO_DATE(CONCAT(%s, '-01'), '%%Y-%%m-%%d')), ct.card_start_time)+1 AS days_from_start,
                    brt.pay,
                    brt.real_pay,
                    brt.pay/brt.buy_num AS avr_pay,
                    brt.buy_date,
                    css.cal_salary 
                FROM cls_tkn_rec_table ctrt 
                LEFT JOIN(
                    SELECT DISTINCT card_id, card_start_time 
                    FROM cards_table ct0 
                    WHERE card_start_time IS NOT NULL
                ) AS ct ON ct.card_id = ctrt.card_id
                LEFT JOIN buy_rec_table brt ON brt.card_id = ctrt.card_id
                LEFT JOIN card_salary_stat css ON css.card_id = ctrt.card_id
                WHERE 
                    ctrt.ins_id = %s 
                    AND ctrt.cls_datetime BETWEEN STR_TO_DATE(CONCAT(%s, '-01'), '%%Y-%%m-%%d') AND LAST_DAY(STR_TO_DATE(CONCAT(%s, '-01'), '%%Y-%%m-%%d'))
                    AND ctrt.cls_type = '常规课程'
                GROUP BY 
                    ctrt.card_id, ctrt.cus_id, ctrt.cus_name, ctrt.ins_id, ctrt.ins_name,
                    ct.card_start_time, brt.pay, brt.real_pay, ctrt.cls_type, ctrt.cls_name,
                    brt.buy_date, brt.buy_num, css.cal_salary

            '''


            cursor.execute(sql,(year_month,ins_id,year_month,year_month,year_month,ins_id,year_month,year_month))
            res=cursor.fetchall()


            sql='''
            select ins_id,ins_name,ins_type from ins_table where ins_id=%s;            
'''
            
            cursor.execute(sql,ins_id)
            res_ins=cursor.fetchone()

            #对全职兼职教练的底薪计算
            if res_ins[2]=='full':
                basic_salary=1500
            elif  res_ins[2]=='part':
                basic_salary=0
            else:
                basic_salary=0


            #查询本月销售数据
            sql='''
                select brt.cus_name,brt.buy_date,brt.card_id,brt.pay,brt.real_pay,ct.cashier_name,brt.comment from buy_rec_table brt
                left join cashier_table ct on ct.cashier_id=brt.cashier_id
                where year(brt.buy_date)=%s and month(brt.buy_date)=%s and brt.cashier_id=
                (select related_cashier_id from ins_table where ins_id=%s)

            '''
            cursor.execute(sql,(year_month.split('-')[0],year_month.split('-')[1],ins_id))
            res_sale=cursor.fetchall()

            if res_sale:
                res_sale_data=self.res_to_dic(res_sale,('cus_name','buy_date','card_id','pay','real_pay','cashier_name','comment'))


            #查询小团课数据
            with open(os.path.join(os.path.dirname((__file__)),'config','salary_config'),'r',encoding='utf-8') as f:
                cfg=json.load(f)
            
            grp_price=float(cfg['课提']['限时课程']['小团课'].split('/')[0])

            # print('小团课单价：',grp_price)

            sql='''
                SELECT  GROUP_CONCAT(DISTINCT ctrt.cus_name ORDER BY ctrt.cus_name SEPARATOR ', ') AS grp_cus_names,ctrt.cls_type,
                '小团课' as cls_name, ctrt.cls_datetime,COUNT(DISTINCT ctrt.cls_datetime) AS cls_tkn_qty,%s as avr_pay_for_ins, ctrt.ins_id,ctrt.ins_name
                FROM cls_tkn_rec_table ctrt
                WHERE 
                    ctrt.ins_id = %s
                    AND YEAR(ctrt.cls_datetime) = %s
                    AND MONTH(ctrt.cls_datetime) =%s
                    AND ctrt.cls_name = '小团课'
                GROUP by ctrt.cls_datetime,ctrt.cls_type,ctrt.cls_name,ctrt.ins_id,ctrt.ins_name

            '''
            cursor.execute(sql,((grp_price,ins_id,year_month.split('-')[0],year_month.split('-')[1])))
            _res_grp=cursor.fetchall()
            print(_res_grp)


            #查询未结算数据
            sql='''
            SELECT css.buy_date,css.card_id,css.buy_cls_name,css.buy_type,css.cashier_id,ct.cashier_name,css.cus_id,
                    css.cus_name,COALESCE(cdt.card_start_time,'未开卡') as card_start_time,
                    COALESCE(DATEDIFF(LAST_DAY(CONCAT(%s, '-01')), cdt.card_start_time)+1,'-') AS days_since_start from card_salary_stat css
            left JOIN cashier_table ct on ct.cashier_id=css.cashier_id
            left join (
                select card_id,max(card_start_time) as card_start_time from cards_table group by card_id
            ) as cdt on cdt.card_id=css.card_id

            where css.cal_salary='no' and css.cashier_id=(
                select it.related_cashier_id from ins_table it where it.ins_id=%s
            )
            HAVING buy_date<=last_day(CONCAT(%s, '-01'));
            '''
            cursor.execute(sql,(year_month,ins_id,year_month))
            _not_cal_salary_list=cursor.fetchall()

            if _not_cal_salary_list:
                cols=('buy_date','card_id','buy_cls_name','buy_type','cashier_id','cashier_name','cus_id','cus_name','card_start_date','days_since_start')
                not_cal_salary_list=self.res_to_dic(_not_cal_salary_list,cols)
            else:
                not_cal_salary_list=None



            if _res_grp:
                cols=('grp_cus_name','cls_type','cls_name','cls_datetime','cls_tkn_qty','avr_pay_for_ins','ins_id','ins_name')
                res_grp=self.res_to_dic(_res_grp,cols)
            else:
                res_grp=None


            if res and res_ins:
                cols=('card_id','cls_type','cls_name','cus_id','cus_name','ins_id','ins_name','cls_tkn_qty','card_start_time','days_from_start','pay','real_pay','avr_pay','buy_date','cal_salary')
                return jsonify({'res':'ok','msg':'获取教练薪酬列表成功','data':self.res_to_dic(res,cols),
                                'data_ins':{'ins_id':res_ins[0],'ins_name':res_ins[1],'ins_type':res_ins[2],'basic_salary':basic_salary},
                                'data_grp':res_grp,
                                'ins_sale_data':res_sale_data,
                                'not_cal_salary_list':not_cal_salary_list})
            else:
                return jsonify({
                'res': 'failed',
                'msg': 'get ins salary data error.'
            })
            
        except Exception as e:
            conn.rollback()
            print('get ins salary data error:', e)
            return jsonify({
                'res': 'failed',
                'msg': str(e)
            })
            
        finally:
            cursor.close()
            conn.close()

    


    def write_cal_ins_salary(self):

        dat=request.json

        dat['cus_id']=dat['客户编码及姓名'][:7].strip()
        dat['cus_name']=dat['客户编码及姓名'][7:].strip()

        del dat['客户编码及姓名']
        data_cols=['cus_id','cus_name','收款日期', '购课卡号','购课流水号','购课类型','购课名称','购课节数', '购课时长（天）', '应收金额', '实收金额','与应收一致', '收款人', '收入类别', '备注','管理员备注','operatorId','operateTime']
        sorted_data={key: dat[key] for key in data_cols}
        values_buy_table=tuple(sorted_data.values()) 
        
        print('salary mark received data:',sorted_data)

        conn=self.connect_mysql()
        cursor=conn.cursor()

        print(sorted_data['cus_id'],sorted_data['cus_name'],sorted_data['收款日期'],sorted_data['购课卡号'],'no',sorted_data['购课流水号'],
                                sorted_data['购课类型'],sorted_data['购课名称'],sorted_data['收款人'],'',sorted_data['operatorId'],sorted_data['operateTime'])

        try:
            #插入购课记录
            sql='''
                insert into card_salary_stat 
                (cus_id,cus_name,buy_date,card_id,cal_salary,buy_flow_id,buy_type,buy_cls_name,cashier_id,comment,operator_id,operate_time)
                values
                (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
                '''
            
            cursor.execute(sql,(sorted_data['cus_id'],sorted_data['cus_name'],sorted_data['收款日期'],sorted_data['购课卡号'],'no',sorted_data['购课流水号'],
                                sorted_data['购课类型'],sorted_data['购课名称'],sorted_data['收款人'],'',sorted_data['operatorId'],sorted_data['operateTime']))
            conn.commit()
            
            return jsonify({'res':'ok','msg':'新增购课记录插入薪酬未结算标记no成功'})

        except Exception as e:
            print('insert into card_salary_stat error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()

        

    def get_group_cus_list(self):
        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            #插入购课记录
            sql='''
                SELECT 
                    cct.cus_id,
                    cit.cus_name,
                    cit.sex,
                    ct.card_id,
                    ct.card_start_time,
                    ct.end_time,
                    ct.card_type,
                    ct.card_name,
                    ct.cls_qty,
                    COALESCE(ctrt.cls_tkn_qty, 0) AS cls_tkn_qty, 
                    ct.cls_qty - COALESCE(ctrt.cls_tkn_qty, 0) AS cls_qty_remain 
                FROM 
                    cards_table ct
                LEFT JOIN 
                    cardholder_card_table cct ON cct.card_id = ct.card_id
                LEFT JOIN 
                    cus_info_table cit ON cit.cus_id = cct.cus_id
                LEFT JOIN (
                    SELECT 
                        card_id,
                        COUNT(*) AS cls_tkn_qty 
                    FROM 
                        cls_tkn_rec_table 
                    GROUP BY 
                        card_id
                ) AS ctrt ON ctrt.card_id = ct.card_id
                WHERE 
                    ct.card_type = '限时课程'
                    AND ct.card_name = '小团课'
                    AND ct.end_time > CURDATE() 
                    AND ct.card_start_time IS NOT NULL;  
            '''
            
            cursor.execute(sql)
            res=cursor.fetchall()
   
            if res:
       
                cols=['cus_id','cus_name','sex','card_id','card_start_time','end_time','card_type','card_name','cls_qty','cls_tkn_qty','cls_qty_remain']
                df=pd.DataFrame(res,columns=cols)
 
                # 将日期字段转换为日期类型

                df['end_time'] = pd.to_datetime(df['end_time'])
              
                # print(self.dic_format2(df,cols,transfer_int_64='yes'))
                return jsonify({'res':'ok','data':self.dic_format2(df,cols,transfer_int_64='yes'),'cols':cols})
  
            else:
                return jsonify({'res':'failed','error':'no group class data'})

        except Exception as e:
            print('get group class data error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()

    def get_activity_cls_info(self):
        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            #插入购课记录
            sql='''
                select ct.card_id,max(end_time) as end_time,ct.cls_qty,coalesce(cal_tkn_table.cls_tkn_qty,0) as cls_tkn_qty, cls_qty-coalesce(cal_tkn_table.cls_tkn_qty,0) as cls_remain_qty, cct.cus_id, cit.cus_name,icct.ins_ids,icct.ins_names from cards_table ct
                left join cardholder_card_table cct on cct.card_id=ct.card_id
                left join cus_info_table cit on cit.cus_id=cct.cus_id
                left join ins_control_cus_table icct on icct.cus_id=cit.cus_id
                left join (select card_id,sum(cls_long) as cls_tkn_qty from cls_tkn_rec_table where cls_type='常规课程' and cls_name='活动赠课' group by card_id) as cal_tkn_table on cal_tkn_table.card_id=ct.card_id
                where card_type='常规课程' and card_name='活动赠课'
                group by ct.card_id, ct.cls_qty,cct.cus_id, cit.cus_name,icct.ins_ids,icct.ins_names;   
            '''
            
            cursor.execute(sql)
            res=cursor.fetchall()
   
            if res:
       
                cols=['card_id','end_time','cls_qty','cls_tkn_qty','cls_remain_qty','cus_id','cus_name','ins_ids','ins_names']
                df=pd.DataFrame(res,columns=cols)
 
                # 将日期字段转换为日期类型

                df['end_time'] = pd.to_datetime(df['end_time'])
              

                return jsonify({'res':'ok','data':self.dic_format2(df,cols,transfer_int_64='yes'),'cols':cols})
  
            else:
                return jsonify({'res':'failed','error':'no activity send cls data.'})

        except Exception as e:
            print('get activity send cls error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()

    def get_cus_continue_buy(self):
        data=request.json
        cus_id=data['cusIdName'][:7]

        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            #插入购课记录
            sql='''
                select cct.cus_id, cit.cus_name,cct.card_id, brt.buy_date,brt.buy_type,brt.buy_cls_name, brt.buy_num, brt.buy_cls_days, ct2.cls_qty,ctrt.cls_tkn_qty,ct.card_start_time,ct.end_time,datediff(ct.end_time,ct.card_start_time) as real_days,icct.ins_names
                from cardholder_card_table cct
                left join (select card_id, max(card_start_time) as card_start_time,max(end_time) as end_time from  cards_table  group by card_id) as ct on ct.card_id=cct.card_id
                left join (SELECT ct3.card_id, ct3.cls_qty
                FROM cards_table ct3
                JOIN (
                    SELECT card_id, MAX(id) as max_id
                    FROM cards_table
                    GROUP BY card_id
                ) ct4 ON ct3.card_id = ct4.card_id AND ct3.id = ct4.max_id) as ct2 on ct2.card_id=cct.card_id
                left join buy_rec_table as brt on brt.card_id=cct.card_id
                left join cus_info_table cit on cit.cus_id=cct.cus_id
                left join ins_control_cus_table icct on icct.cus_id=cct.cus_id
                left join (select card_id,sum(cls_long) as cls_tkn_qty from cls_tkn_rec_table group by card_id) as  ctrt on ctrt.card_id=cct.card_id
                where cct.cus_id=%s
                HAVING brt.buy_type='限时课程' and brt.buy_cls_name='私教' and brt.buy_num=15 and brt.buy_cls_days=30 and ct.card_start_time is not null and ct.end_time is not null
                       or brt.buy_type='限时课程' and brt.buy_cls_name='私教' and brt.buy_num=30 and brt.buy_cls_days=30 and ct.card_start_time is not null and ct.end_time is not null
                order by ct.card_start_time desc,brt.buy_date desc,ct.end_time desc 
                limit 3
            '''
            
            cursor.execute(sql,cus_id)
            res=cursor.fetchall()
   
            if res:
       
                cols=['cus_id','cus_name','card_id','buy_date','buy_type','buy_cls_name','buy_num','buy_cls_days','cls_qty','cls_tkn_qty','card_start_time','end_time','real_days','ins_names']
                df=pd.DataFrame(res,columns=cols)
 
                # 将日期字段转换为日期类型
                df['buy_date'] = pd.to_datetime(df['buy_date'])
                df['end_time'] = pd.to_datetime(df['end_time'])

                # 初始化 continue_buy 列
                df['continue_buy'] = 'no'

                # 初始化 latest_3_times_continue_buy 变量
                latest_3_times_continue_buy = 'yes'

                # 检查前两行条件
                for i in range(2):
                    if i + 1 < len(df) and pd.notna(df.loc[i + 1, 'end_time']):  # 确保有下一行并且 end_time 不为 NaT
                        end_time_plus_7 = df.loc[i + 1, 'end_time'] + datetime.timedelta(days=7)
                        if df.loc[i, 'buy_date'] <= end_time_plus_7:
                            df.loc[i, 'continue_buy'] = 'yes'
                        else:
                        
                            latest_3_times_continue_buy = 'no'
                    else:
                        latest_3_times_continue_buy = 'no'

                # 打印结果
                # print('df:',df)
                # print("latest_3_times_continue_buy:", latest_3_times_continue_buy)

                cols.append('continue_buy')

                return jsonify({'res':'ok','data':self.dic_format2(df,cols,transfer_int_64='yes'),'cols':cols,'latest_3_times_continue_buy':latest_3_times_continue_buy})
  
            else:
                return jsonify({'res':'failed','error':'no cus continue buy data.'})

        except Exception as e:
            print('cus continue buy data error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()

    def get_due_9point9_data(self):
        data=request.json
        cls_name=data['cls_name']
        year=int(data['today'].split('-')[0])
        month=int(data['today'].split('-')[1])

        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            #插入购课记录
            sql='''
                SELECT
                    cct.cus_id,
                    cit.cus_name,
                    cit.sex,
                    ct.card_id,
                    ct.card_type,
                    ct.card_name,
                    ct.cls_qty,
                    ct.card_start_time,
                    ct.prd,
                    ct.end_time,
                    COALESCE(ctrt.cls_datetime,0),
                    icct.ins_ids,
                    icct.ins_names
                FROM
                    `cards_table` ct
                LEFT JOIN cls_tkn_rec_table ctrt ON
                    ctrt.card_id = ct.card_id
                LEFT JOIN cardholder_card_table cct ON
                    cct.card_id = ct.card_id
                LEFT JOIN cus_info_table cit ON
                    cit.cus_id = cct.cus_id
                LEFT JOIN ins_control_cus_table icct ON
                    icct.cus_id = cct.cus_id
                WHERE
                    card_name = %s AND YEAR(ct.end_time) = %s AND MONTH(ct.end_time) = %s
                ORDER BY ct.end_time;
            '''
            
            cursor.execute(sql,(cls_name,year,month))
            res=cursor.fetchall()
   
            if res:
                cols=('客户id','客户姓名','性别','卡号','购课类型','课程名称','购课节数','课程开始时间','课程有效时间','课程结束时间','上课日期','教练id','教练姓名')

                return jsonify({'res':'ok','data':self.res_to_dic(res,cols),'cols':cols})
  
            else:
                return jsonify({'res':'failed','error':'no 9.9 cls due data.'})

        except Exception as e:
            print('9.9 cls due data:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()

    # def res_to_dic(self,res,keys):
    #     dic_arr=[]
    #     for n,item in enumerate(res):
    #         dic={}
    #         for k,value in enumerate(item):
    #             dic[keys[k]]=value
    #         dic_arr.append(dic)
    #     return dic_arr
    
    def deal_transfer_lmt_to_dms(self):
        data=request.json
        data_origin=data['origin_card']
        data_dms=data['dms_card']
        data_dms_buy=data['new_dms_buy']

        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            #插入购课记录
            sql='''
                 insert into buy_rec_table
                (cus_id,cus_name,buy_date,card_id,buy_flow_id,buy_type,buy_cls_name,buy_num,buy_cls_days,valid_time,pay,real_pay,cashier_id,income_type,comment,operator_id,operate_time)
                values
                (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
            '''
            
            cursor.execute(sql,(data_dms_buy['cus_id'],data_dms_buy['cus_name'],data_dms_buy['buy_date'],data_dms_buy['card_id'],data_dms_buy['buy_flow_id'],
                                data_dms_buy['buy_type'],data_dms_buy['buy_cls_name'],data_dms_buy['buy_num'],data_dms_buy['buy_cls_days'],data_dms_buy['valid_time'],data_dms_buy['pay'],
                                data_dms_buy['real_pay'],data_dms_buy['cashier_id'],data_dms_buy['income_type'],data_dms_buy['comment'],data_dms_buy['operator_id'],
                                data_dms_buy['operate_time']))


            #插入客户持卡表
            sql='''
                insert into cardholder_card_table
                (cus_id,card_id,relation_time,cmt)
                values
                (%s,%s,%s,%s)
            '''

            cursor.execute(sql,(data_dms_buy['cus_id'],data_dms_buy['card_id'],data_dms_buy['operate_time'],data_dms_buy['comment']))            
            
            #插入原来的购课变动
            sql='''
                insert into cards_table (card_id,card_type,card_name,cls_qty,card_start_time,prd,valid_time,end_time,cmt,opr_id,opr_time) values
                (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) 
            '''

            cursor.execute(sql,(data_origin['card_id'],data_origin['card_type'],data_origin['card_name'],data_origin['cls_qty'],
                                 data_origin['card_start_time'],data_origin['prd'],data_origin['valid_time'],data_origin['end_time'],data_origin['cmt'],
                                 data_origin['opr_id'],data_origin['opr_time']))

            #插入dms信息，在cards_table中
            sql='''
                insert into cards_table (card_id,card_type,card_name,cls_qty,card_start_time,prd,valid_time,end_time,cmt,opr_id,opr_time) values
                (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) 
            '''
            cursor.execute(sql,(data_dms['card_id'],data_dms['card_type'],data_dms['card_name'],data_dms['cls_qty'],
                                 data_dms['card_start_time'],data_dms['prd'],data_dms['valid_time'],data_dms['end_time'],data_dms['cmt'],
                                 data_dms['opr_id'],data_dms['opr_time']))
            
            conn.commit()
         
            return jsonify({'res':'ok','data':'transfer successfully'})


        except Exception as e:
            print('get all valid data error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()

    def new_cus_count_by_month(self):
        data=request.json
        s_date=data['sDate']
        e_date=data['eDate']
        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            sql='''
                SELECT 
                    months.year,
                    months.month,
                    COALESCE(counts.count_add_month, 0) AS count_add_month
                FROM (
                    SELECT 
                        YEAR(DATE_ADD(%s, INTERVAL seq MONTH)) AS year,
                        MONTH(DATE_ADD(%s, INTERVAL seq MONTH)) AS month
                    FROM (
                        SELECT 0 AS seq UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 
                        UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 
                        UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
                        UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
                    ) AS seq
                    WHERE DATE_ADD(%s, INTERVAL seq MONTH) <= %s
                ) AS months
                LEFT JOIN (
                    SELECT 
                        YEAR(add_datetime) AS year,
                        MONTH(add_datetime) AS month,
                        COUNT(*) AS count_add_month 
                    FROM 
                        cus_info_table 
                    WHERE 
                        date(add_datetime) BETWEEN %s AND %s
                    GROUP BY 
                        YEAR(add_datetime), MONTH(add_datetime)
                ) AS counts ON months.year = counts.year AND months.month = counts.month
                ORDER BY months.year, months.month;
            '''
            
            cursor.execute(sql,(s_date,s_date,s_date,e_date,s_date,e_date))
            res=cursor.fetchall()
         
            if res:
                return jsonify({'res':'ok','data':res})
            else:
                return jsonify({'res':'failed','error':'no monthly added cus count data'})


        except Exception as e:
            print('get monthly added cus count data error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()
 
    def get_day_cls_data(self):
        data=request.json
        s_date=data['startDate']
        e_date=data['endDate']
        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            sql='''
                select cus_id,cus_name,cls_datetime,cls_type,cls_name,ins_id,ins_name from cls_tkn_rec_table 
                where date(cls_datetime) BETWEEN %s and %s order by cls_datetime;
            '''
            
            cursor.execute(sql,(s_date,e_date))
            res=cursor.fetchall()
         
            if res:
                return jsonify({'res':'ok','data':res})
            else:
                return jsonify({'res':'failed','error':'no day cls taken data'})


        except Exception as e:
            print('get  day cls taken data error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()


    def get_valid_cards(self,data=''):
        if data=='':
            data=request.json
        today=data['today']
        cus_id_name=data['cus_id']
        conn=self.connect_mysql()
        cursor=conn.cursor()

        def filter_group(group):
            # 如果card_id有重复
            if len(group) > 1:
                # 选择符合 cls_qty < buy_num 的记录
                # filtered = group[(group['cls_qty'] < group['buy_num']) & (group['id'] == group['id'].max())]
                filtered = group[group['id'] == group['id'].max()]

                if not filtered.empty:
                    return filtered
            # 如果card_id没有重复，则直接返回该记录
            return group

        try:
            sql='''
                SELECT DISTINCT	
                    brt.cus_id,
                    brt.cus_name,
                    brt.buy_date,
                    cat.card_id,
                    brt.buy_flow_id,
                    brt.buy_type,
                    brt.buy_cls_name,
                    brt.buy_num,
                    cat.cls_qty,
                    brt.buy_cls_days,
                    brt.valid_time,
                    brt.pay,
                    brt.real_pay,
                    brt.cashier_id,
                    ct.cashier_name,
                    cit.sex,
                    COALESCE(cat.card_start_time, '-') AS start_time,
                    COALESCE(cat.end_time, '-') AS end_time,
                    COALESCE(ctrt.cls_tkn_qty, 0) AS cls_tkn_qty,
                    (
                        SELECT GROUP_CONCAT(DISTINCT CONCAT(cc.cus_id, cit2.cus_name)) 
                        FROM cardholder_card_table cc
                        JOIN cus_info_table cit2 ON cc.cus_id = cit2.cus_id
                        WHERE cc.card_id = brt.card_id
                    ) AS co_holder,
                    cat.id,
                    COALESCE(transfered_table.transfered_num,0) AS transfered_num
                FROM 
                    cards_table cat -- 使用正确的表名
                LEFT JOIN 
                    buy_rec_table brt ON brt.card_id = cat.card_id
                LEFT JOIN 
                    (SELECT cashier_id, cashier_name FROM cashier_table) AS ct 
                    ON ct.cashier_id = brt.cashier_id
                LEFT JOIN 
                    (SELECT cus_id, sex FROM cus_info_table) AS cit 
                    ON cit.cus_id = brt.cus_id
                LEFT JOIN 
                    (SELECT COALESCE(sum(cls_long), 0) AS cls_tkn_qty, card_id 
                    FROM cls_tkn_rec_table GROUP BY card_id) AS ctrt 
                    ON ctrt.card_id = brt.card_id              
                LEFT JOIN(
                    SELECT card_id, COALESCE(COUNT(card_id),0) AS transfered_num FROM cards_table WHERE cmt = '部分已转换成DMS课程'
                    GROUP BY
                        card_id
                ) AS transfered_table
                ON
                    cat.card_id = transfered_table.card_id                          
                WHERE 
                    cat.card_id IN (
                        SELECT card_id 
                        FROM cardholder_card_table 
                        WHERE cus_id = %s
                    )
                    AND
                    %s BETWEEN cat.card_start_time AND cat.end_time
                GROUP BY 
                    brt.cus_id,
                    brt.cus_name,
                    brt.buy_date,
                    cat.card_id,
                    brt.buy_flow_id,
                    brt.buy_type,
                    brt.buy_cls_name,
                    brt.buy_num,
                    cat.cls_qty,
                    brt.buy_cls_days,
                    brt.valid_time,
                    brt.pay,
                    brt.real_pay,
                    brt.cashier_id,
                    ct.cashier_name,
                    cit.sex,
                    cat.card_start_time,
                    cat.end_time,
                    ctrt.cls_tkn_qty,
                    cat.id
                 HAVING COALESCE(ctrt.cls_tkn_qty, 0) < cat.cls_qty
            '''
            
            cursor.execute(sql,(cus_id_name[:7],today))
            res=cursor.fetchall()
         
            if res:
                columns=['cus_id','cus_name','buy_date','card_id','buy_flow_id','buy_type','buy_cls_name','buy_num','cls_qty','buy_cls_days','valid_time',
                                             'pay','real_pay','cashier_id','cashier_name','sex','start_time','end_time','cls_tkn_qty','co_holder','id','transfered_num']
                df=pd.DataFrame(res,columns=columns)
                filtered_df = df.groupby('card_id', group_keys=False).apply(filter_group)
                filtered_df.drop_duplicates(inplace=True)
                filtered_df.reset_index(inplace=True)
                cols=('客户ID','客户姓名','购课日期','卡号','购课流水','购课类型','课程名称','购课节数','实际节数','课程天数','有效时间段','应付金额','实付金额',
                    '收款人ID','收款人姓名','性别','课程开始时间','课程结束时间','已上节数','共同持卡人','id','已转换节数')
                return jsonify({'res':'ok','data':self.dic_format(filtered_df,columns,transfer_int_64='yes'),'cols':cols})


            else:
                return jsonify({'res':'failed','error':'no valid data'})


        except Exception as e:
            print('get all valid data error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()

    def get_all_continue_buy_cls_rec(self,data=''):
        if data=='':
            data=request.json
        today=data['today']
        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            sql='''
                select chct.cus_id,cit.cus_name,ct.card_id, ct.card_type,ct.card_name,min(ct.cls_qty),ct.card_start_time,max(ct.end_time) as end_time ,ctrt.cls_tkn_qty, icct.ins_ids, icct.ins_names
                from cards_table ct
                left join (select card_id, sum(cls_long) as cls_tkn_qty from cls_tkn_rec_table group by card_id) as ctrt on ctrt.card_id=ct.card_id
                left join cardholder_card_table chct on chct.card_id=ct.card_id
                left join cus_info_table cit on cit.cus_id=chct.cus_id
                left join ins_control_cus_table icct on icct.cus_id=chct.cus_id
                where ct.card_type='限时课程' and %s BETWEEN ct.card_start_time and ct.end_time
                group by chct.cus_id,cit.cus_name,ct.card_id, ct.card_type,ct.card_name,ct.card_start_time,ctrt.cls_tkn_qty, icct.ins_ids, icct.ins_names
                HAVING cls_tkn_qty<= min(cls_qty)
            '''
            cursor.execute(sql,today)
            res=cursor.fetchall()
         
            if res:
                df=pd.DataFrame(res,columns=['cus_id','cus_name','card_id','card_type','card_name','cls_qty','card_start_time','end_time','cls_tkn_qty','ins_ids','ins_names'])
        
                df['continue_buy']=df['card_id'].apply(lambda x: 'yes' if self.get_continue_buy_cls_rec({'card_id': x}).get_json().get('continue_buy_recent_3_times') == 'yes' else 'no')

                return jsonify({'res':'ok','data':self.dic_format(df,['cus_id','cus_name','card_id','card_type','card_name','cls_qty','card_start_time','end_time','cls_tkn_qty','ins_ids','ins_names'],transfer_int_64='yes')})
                # return jsonify({'res':'ok','data':df.values.tolist()})


            else:
                return jsonify({'res':'failed','error':'no all continue buy data'})


        except Exception as e:
            print('get all limit prd continue buy data error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()
         

    def get_continue_buy_cls_rec(self,data=''):
        if data=='':
            data=request.json
        card_id=data['card_id']
        # print(data)
        conn=self.connect_mysql()
        cursor=conn.cursor()
       
        try:    
            sql='''
                select brt.cus_id, brt.cus_name,brt.buy_date,brt.card_id,brt.buy_flow_id,brt.buy_num,ct.card_start_time,ct.end_time,ctrt.cls_tkn_qty from buy_rec_table brt
                left join (select card_id, max(card_start_time) as card_start_time, max(end_time) as end_time from  cards_table group by card_id) as ct on ct.card_id=brt.card_id
                left join (select card_id,sum(cls_long) as cls_tkn_qty from cls_tkn_rec_table group by card_id) as ctrt on ctrt.card_id=brt.card_id
                where buy_type=(select buy_type from buy_rec_table where card_id=%s) and buy_cls_name=(select buy_cls_name from buy_rec_table where card_id=%s)  and buy_num='15' and buy_cls_days='30' and cus_id=(select cus_id from buy_rec_table where card_id=%s) and buy_date<=(select buy_date from buy_rec_table where card_id=%s)
                HAVING ct.card_start_time is not null and ct.end_time is not null
                order by ct.end_time desc 
                limit 3;
            '''

            cursor.execute(sql,(card_id,card_id,card_id,card_id))
            res=cursor.fetchall()   

            if res:
                df=pd.DataFrame(res, columns=['cus_id', 'cus_name', 'buy_date', 'card_id', 'buy_flow_id', 'buy_num', 'card_start_time', 'end_time', 'cls_tkn_qty'])
                results = []
                for i in range(len(df)-1):
                    next_end_time = pd.to_datetime(df.iloc[i+1]['end_time']) + pd.DateOffset(days=6)
                    current_buy_date = pd.to_datetime(df.iloc[i]['buy_date'])
                    
                    if current_buy_date <= next_end_time or current_buy_date <= pd.to_datetime(df.iloc[i+1]['end_time']):
                        results.append('yes')
                    else:
                        results.append('no')

                # 处理最后一行数据
                results.append('yes')

                df['continue_buy'] = results

                df_return=self.dic_format(df,['cus_id', 'cus_name', 'buy_date', 'card_id', 'buy_flow_id', 'buy_num', 'card_start_time', 'end_time', 'cls_tkn_qty'],transfer_int_64='yes')
                # print(df_return)
                if df.shape[0]<3:
                    return jsonify({'res':'failed','error':'continue buy less than 3 recs'})
                else: 
                    if 'no' in df['continue_buy'].values:
                        return jsonify({'res':'failed','error':'not continue buy in recent 3 recs'})
                    else:
                        # print('all yes')
                        return jsonify({'res':'ok','data':df_return,'continue_buy_recent_3_times':'yes'})
            else:
                return jsonify({'res':'failed','error':'no limit prd buy data'})


        except Exception as e:
            print('get limit prd continue buy data error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()

    def get_9_point_9_yuan_cls(self):
        # data=request.json
        conn=self.connect_mysql()
        cursor=conn.cursor()
        today=datetime.datetime.now().strftime('%Y-%m-%d')
       
        try:    
            sql='''
                SELECT brt.cus_id, brt.cus_name, brt.card_id, brt.buy_flow_id, brt.buy_type, brt.buy_cls_name, brt.buy_num, ct.card_start_time, ct.end_time, ctrt.cls_datetime, icct.ins_ids, icct.ins_names 
                FROM buy_rec_table brt 
                LEFT JOIN cards_table ct ON ct.card_id = brt.card_id
                LEFT JOIN ins_control_cus_table icct ON icct.cus_id = brt.cus_id
                LEFT JOIN cls_tkn_rec_table ctrt ON ctrt.card_id = brt.card_id
                WHERE brt.buy_type = '常规课程' AND brt.buy_cls_name = '9.9元私教'
                HAVING ctrt.cls_datetime IS NULL AND %s BETWEEN ct.card_start_time AND DATE_SUB(ct.end_time, INTERVAL 1 DAY);

            '''

            cursor.execute(sql,today)
            res=cursor.fetchall()   
            # print(res)

            if res:
                return jsonify({'res':'ok','data':res})
            else:
                return jsonify({'res':'failed','error':'no 9.9yuan data'})


        except Exception as e:
            print('get 9.9yuan cls list error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()

    def edit_pass_due_card_new_end_date(self):
        data=request.json
        conn=self.connect_mysql()
        cursor=conn.cursor()
        current_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        data['opr_time']=current_time
        # data['cmt']=''
        # print(data)

        #查询该卡第一条记录中的备注cmt内容
        sql='''
            select cmt from cards_table where card_id=%s order by id limit 1;
        '''
        cursor.execute(sql,data['card_id'])
        res=cursor.fetchone()
        cmt=res[0] 

        params = (data['card_id'], data['cls_type'], data['cls_name'], data['cls_qty'], data['card_start_time'], data['prd'], data['end_time'], cmt, data['ins_id'], data['opr_time'])
        # print(params)

        try:    
            sql='''
                insert into cards_table (card_id,card_type,card_name,cls_qty,card_start_time,prd,end_time,cmt,opr_id,opr_time) values
                (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) 

            '''

            cursor.execute(sql,params)
            conn.commit()

            # if res:
            #     return jsonify({'res':'ok','data':res})
            # else:
            #     return jsonify({'res':'failed','error':'no cus hour qty'})
            return jsonify({'res':'ok','data':data})

        except Exception as e:
            print('edit pass due card new end_time error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()
    

    def get_cus_cls_hour_qty(self):
        data=request.json
        cus_id=data['cusIdName'][:7]
        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            sql='''
                select cus_id,cus_name,hour(cls_datetime) as hour,count(HOUR(cls_datetime)) as hour_qty from cls_tkn_rec_table where cus_id=%s group by cus_id,cus_name,hour(cls_datetime);

            '''
            cursor.execute(sql,cus_id)
            res=cursor.fetchall()
            if res:
                return jsonify({'res':'ok','data':res})
            else:
                return jsonify({'res':'failed','error':'no cus hour qty'})

        except Exception as e:
            print('get cus hour qty data error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()

    @login_required
    def get_buy_stat_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./get_buy_stat.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def ins_salary_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./ins_salary.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)


    @login_required
    def salary_index_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./salary_index.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def show_day_cls_stat(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_day_cls_stat.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

        
    @login_required
    def show_9point9_due_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_9point9_due_page.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def show_all_after_due_cus_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_all_after_due_cus.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def transfer_cls_type(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./transfer_cls_type.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def show_continue_buy_list(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_continue_buy_list.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)


    @login_required
    def edit_pass_due_card(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./edit_pass_due_card.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    def get_trial_problem(self):
        data=request.json
        cus_id_name=data['cusIdName']
        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            sql='''
                select trial_problem from trial_cls_table where formal_cus_id_name=%s;

            '''
            cursor.execute(sql,cus_id_name)
            res=cursor.fetchone()
            if res:
                return jsonify({'res':'ok','data':res})
            else:
                return jsonify({'res':'failed','error':'no trial problem'})

        except Exception as e:
            print('get trial problem data error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
        finally:
            cursor.close()
            conn.close()

    def get_pass_due_rec(self):
        data=request.json
        cus_id_name=data['cus_name']
        cls_name=data['cls_name']
        cls_type=data['cls_type']
        cus_id,cus_name=cus_id_name[:7],cus_id_name[7:]
        conn=self.connect_mysql()
        cursor=conn.cursor()


        def filter_group(group):
            # 如果card_id有重复
            if len(group) > 1:
                # 选择符合 cls_qty < buy_num 的记录
                # filtered = group[(group['cls_qty'] < group['buy_num']) & (group['id'] == group['id'].max())]
                filtered = group[group['id'] == group['id'].max()]

                if not filtered.empty:
                    return filtered
            # 如果card_id没有重复，则直接返回该记录
            return group

        try:
            sql='''
            SELECT DISTINCT cct.cus_id, cit.cus_name, cct.card_id, ct.card_type,ct.card_name, ct.card_start_time ,ct.end_time,ct.cls_qty,ct.prd,ct.id
            FROM `cardholder_card_table` cct 
            left join cus_info_table cit on cit.cus_id=cct.cus_id
            LEFT JOIN (SELECT card_id, card_name, card_start_time, card_type, cls_qty, max(end_time) as end_time, prd,id
                    FROM cards_table ct group by card_id,card_name,card_start_time ,card_type,cls_qty, prd,id) ct 
            ON ct.card_id = cct.card_id 
            WHERE cct.cus_id = %s
            HAVING card_type=%s and card_name=%s
            ORDER BY ct.end_time DESC;
            '''
            cursor.execute(sql,(cus_id,cls_type,cls_name))
            res=cursor.fetchall()

            if res:
                en_cols=('cus_id','cus_name','card_id','card_type','card_name','card_start_time','end_time','cls_qty','prd','id')
                df=pd.DataFrame(res,columns=en_cols)
                filtered_df = df.groupby('card_id', group_keys=False).apply(filter_group)
                filtered_df.drop_duplicates(inplace=True)
                filtered_df.reset_index(inplace=True)
                return jsonify({'res':'ok','data':filtered_df.values.tolist()})
            else:
                return jsonify({'res':'failed','error':'no over due data'})

            
        except Exception as e:
            print('get pass due data error in get_pass_due_rec() :',e)
            return jsonify({'res':'failed'})
        finally:
            cursor.close()
            conn.close()

    def get_after_duedate_data(self):
        conn=self.connect_mysql()
        cursor=conn.cursor()
        result={}
        try:
            #营收金额
            sql='''
                SELECT 
                    cit.cus_id, 
                    cit.cus_name, 
                    MAX(c1.end_time) AS card_end_date,
                    MAX(c1.max_cls_date) AS max_cls_date,
                    CASE
                        WHEN MAX(c1.qty_remain)>0 THEN MAX(c1.end_time)
                        ELSE MAX(c1.max_cls_date)
                    END AS latest_end_date, 
                    DATEDIFF(
                        CURDATE(), 
                        CASE
                            WHEN MAX(c1.qty_remain)>0 THEN MAX(c1.end_time)
                            ELSE MAX(c1.max_cls_date)
                        END) AS days_diff,
                    MAX(brt1.buy_date) AS latest_buy_date,
                    MIN(c1.qty_remain) AS qty_remain,
                    MAX(icct.ins_ids) AS ins_ids, 
                    MAX(icct.ins_names) AS ins_names,
                    '限时课程' AS cls_type,
					MAX(regular_rec.card_id) AS valid_other_card_id,
					c1.buy_date AS this_buy_date					
                FROM 
                    cus_info_table cit
                LEFT JOIN (
                    SELECT 
                        brt.buy_date, 
                        brt.card_id, 
                        brt.buy_type, 
                        brt.buy_num, 
                        COALESCE(ctrt.tkn_qty, 0) AS tkn_qty, 
                        brt.buy_num - COALESCE(ctrt.tkn_qty, 0) AS qty_remain, 
                        ctrt.max_cls_date, 
                        brt.cus_id, 
                        brt.cus_name, 
                        ct.end_time,
						ct.card_start_time
                    FROM 
                        buy_rec_table brt
                    LEFT JOIN 
                        cards_table ct ON ct.card_id = brt.card_id
                    LEFT JOIN (
                        SELECT 
                            card_id, 
                            COUNT(card_id) AS tkn_qty, 
                            MAX(DATE(cls_datetime)) AS max_cls_date 
                        FROM 
                            cls_tkn_rec_table 
                        GROUP BY 
                            card_id
                    ) AS ctrt ON ctrt.card_id = brt.card_id
                    WHERE 
                        brt.buy_type = '限时课程'
                ) AS c1 ON c1.cus_id = cit.cus_id
                LEFT JOIN 
                    buy_rec_table brt1 ON brt1.cus_id = cit.cus_id
                LEFT JOIN 
                    ins_control_cus_table icct ON icct.cus_id = cit.cus_id
                LEFT JOIN(
                       select DISTINCT brt3.card_id,chct3.cus_id,ct3.end_time,brt3.buy_num-ctrt3.cls_tkn_qty
                        from buy_rec_table brt3
                        left join cards_table ct3 on ct3.card_id=brt3.card_id
                        left join (select card_id, COALESCE(sum(cls_long),0) as cls_tkn_qty from cls_tkn_rec_table 
                        group by card_id ) as  ctrt3 on ctrt3.card_id=ct3.card_id
                        left join cardholder_card_table chct3 on chct3.card_id=brt3.card_id
                        where brt3.buy_type='常规课程' and  brt3.buy_num-ctrt3.cls_tkn_qty>0
                    ) AS regular_rec ON regular_rec.cus_id=cit.cus_id
                GROUP BY  cit.cus_id, cit.cus_name,c1.buy_date
                HAVING
                    (card_end_date < CURDATE() AND valid_other_card_id IS NULL AND this_buy_date >= latest_buy_date) 
    OR (card_end_date >= CURDATE() AND qty_remain <= 0 AND valid_other_card_id IS NULL AND this_buy_date >= latest_buy_date)

                UNION

                SELECT 
                    cit.cus_id, 
                    cit.cus_name, 
                    MAX(c1.end_time) AS card_end_date,
                    MAX(c1.max_cls_date) AS max_cls_date,
                    CASE
                        WHEN MAX(c1.qty_remain)>0 THEN MAX(c1.end_time)
                        ELSE MAX(c1.max_cls_date)
                    END AS latest_end_date,
                    DATEDIFF(
                        CURDATE(), 
                        CASE
                            WHEN MAX(c1.qty_remain)>0 THEN MAX(c1.end_time)
                            ELSE MAX(c1.max_cls_date)
                        END) AS days_diff, 
                    MAX(brt1.buy_date) AS latest_buy_date, 
                    MIN(c1.qty_remain) AS qty_remain, 
                    MAX(icct.ins_ids) AS ins_ids, 
                    MAX(icct.ins_names) AS ins_names,
                    '常规课程' AS cls_type,
                    MAX(lmt_rec.card_id) AS valid_other_card_id,
					c1.buy_date AS this_buy_date	
                FROM 
                    cus_info_table cit
                LEFT JOIN (
                    SELECT 
                        brt.buy_date, 
                        brt.card_id, 
                        brt.buy_type, 
                        brt.buy_num, 
                        COALESCE(ctrt.tkn_qty, 0) AS tkn_qty, 
                        brt.buy_num - COALESCE(ctrt.tkn_qty, 0) AS qty_remain,
                        ctrt.max_cls_date, 
                        brt.cus_id, 
                        brt.cus_name, 
                        ct.end_time 
                    FROM 
                        buy_rec_table brt
                    LEFT JOIN 
                        cards_table ct ON ct.card_id = brt.card_id
                    LEFT JOIN (
                        SELECT 
                            card_id, 
                            COUNT(card_id) AS tkn_qty, 
                            MAX(DATE(cls_datetime)) AS max_cls_date 
                        FROM 
                            cls_tkn_rec_table 
                        GROUP BY 
                            card_id
                    ) AS ctrt ON ctrt.card_id = brt.card_id
                    WHERE 
                        brt.buy_type = '常规课程'
                ) AS c1 ON c1.cus_id = cit.cus_id
                LEFT JOIN 
                    buy_rec_table brt1 ON brt1.cus_id = cit.cus_id
                LEFT JOIN 
                    ins_control_cus_table icct ON icct.cus_id = cit.cus_id
				LEFT JOIN(
					select DISTINCT brt2.card_id,chct.cus_id,ct2.end_time,brt2.buy_num-ctrt2.cls_tkn_qty
					from buy_rec_table brt2
					left join cards_table ct2 on ct2.card_id=brt2.card_id
					left join (select card_id, COALESCE(sum(cls_long),0) as cls_tkn_qty from cls_tkn_rec_table group by card_id ) as  ctrt2 on ctrt2.card_id=ct2.card_id
					left join cardholder_card_table chct on chct.card_id=brt2.card_id
					where brt2.buy_type='限时课程' and ct2.end_time<curdate() and brt2.buy_num-ctrt2.cls_tkn_qty>0
				) AS lmt_rec ON lmt_rec.cus_id=cit.cus_id
                GROUP BY 
                    cit.cus_id, cit.cus_name,c1.buy_date
                HAVING 
                    qty_remain IS NOT NULL AND valid_other_card_id IS NULL AND latest_buy_date < latest_end_date AND this_buy_date=latest_buy_date
                ORDER BY days_diff
            '''
            cursor.execute(sql)
            res=cursor.fetchall() 
            if res:
                #处理res，当限时和常规都有名字时，去掉常规的数据，仅保留限时数据
                # 创建DataFrame
                df_all = pd.DataFrame(res)
                df_all.columns=['cus_id','cus_name','card_end_date','max_cls_date','latest_end_date','days_diff','latest_buy_date','qty_remain','ins_ids','ins_names','cls_type','valid_lmt_card_id','this_buy_date']
                df=df_all[['cus_id','cus_name','card_end_date','max_cls_date','latest_end_date','days_diff','latest_buy_date','qty_remain','ins_ids','ins_names','cls_type']]
                # 将日期列转换为日期类型
                df['latest_end_date'] = pd.to_datetime(df['latest_end_date'])
                df['latest_buy_date'] = pd.to_datetime(df['latest_buy_date'])

                # 第1、2步处理
                ids_to_keep = []
                for cus_id, group in df.groupby('cus_id'):
                    if len(group) > 1:  # 当相同的cus_id同时存在限时课程和常规课程时
                        regular_course = group[group['cls_type'] == '常规课程']
                        limited_course = group[group['cls_type'] == '限时课程']

                        if not regular_course.empty and regular_course['qty_remain'].iloc[0] <= 0:
                            ids_to_keep.extend(limited_course.index)
                        elif not regular_course.empty and regular_course['qty_remain'].iloc[0] > 0:
                            continue  # 删除限时课程和常规课程的记录
                        else:
                            ids_to_keep.extend(group.index)
                    else:
                        ids_to_keep.extend(group.index)

                intermediate_df = df.loc[ids_to_keep]

                # 第3步处理
                final_df = intermediate_df[~((intermediate_df['cls_type'] == '常规课程') & (intermediate_df['qty_remain'] > 0))]
                res_out=final_df.values.tolist()
                # print(res_out)
                cols=['cus_id','cus_name','card_end_date','max_cls_date','latest_end_date','days_diff','latest_buy_date','qty_remain','ins_ids','ins_names','cls_type']
                # result['income_by_month']=self.res_to_dic(res,cols)
                result['due_data']=self.res_to_dic(res_out,cols)
            else:
                result['due_data']=None           

            return jsonify({'res':'ok','data':result})
            
        except Exception as e:
            print('get due data error:',e)
            return jsonify({'res':'failed','error':str(e)})
    
    def get_business_data_by_month(self):
        conn=self.connect_mysql()
        cursor=conn.cursor()
        result={}
        try:
            #营收金额
            sql='''
                SELECT * FROM (
                    SELECT 
                        DATE_FORMAT(buy_date, '%Y-%m') AS month, 
                        SUM(real_pay) AS total_real_pay
                    FROM 
                        buy_rec_table
                    WHERE 
                        buy_date >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
                    GROUP BY 
                        DATE_FORMAT(buy_date, '%Y-%m')
                    ORDER BY 
                        month DESC
                    LIMIT 12
                ) AS subquery
                ORDER BY month ASC;  -- 外层再按升序排列
            '''
            cursor.execute(sql)
            res=cursor.fetchall() 
            if res:
                cols=['month','real_pay']
                result['income_by_month']=self.res_to_dic(res,cols)
            else:
                result['income_by_month']=None

            #上课节数
            sql='''
                select * from (
                    SELECT 
                    DATE_FORMAT(ctrt.cls_datetime, '%Y-%m') AS month, 
                    COUNT(ctrt.cls_datetime) AS cls_tkn_qty 
                        FROM 
                            cls_tkn_rec_table ctrt 
                        WHERE 
                            ctrt.cls_datetime >= DATE_SUB(NOW(), INTERVAL 12 MONTH)  -- 限制为最近12个月的数据
                        GROUP BY 
                            DATE_FORMAT(ctrt.cls_datetime, '%Y-%m') 
                        ORDER BY 
                            month desc  -- 按月份升序排列（从最早到最近）
                        LIMIT 12
                ) as subquery
                order by month asc;
            '''
            cursor.execute(sql)
            res=cursor.fetchall() 
            if res:
                cols=['month','cls_tkn_qty']
                result['cls_qty_by_month']=self.res_to_dic(res,cols)
            else:
                result['cls_qty_by_month']=None

            
            #体验课及出单数
            sql='''
                SELECT 
                    DATE_FORMAT(tct.trial_datetime, '%Y-%m') AS month, 
                    COUNT(tct.trial_cus_name) AS trial_qty,
                    COALESCE(SUM(CASE WHEN tct.deal_yn = '是' THEN 1 ELSE 0 END), 0) AS dealed_qty, 
                    COALESCE(SUM(CASE WHEN tct.deal_yn = '是' THEN 1 ELSE 0 END) / COUNT(tct.trial_cus_name), 0) AS dealed_rate 
                FROM 
                    trial_cls_table tct
                WHERE 
                    tct.trial_datetime >= DATE_FORMAT(DATE_SUB(CURRENT_DATE(), INTERVAL 12 MONTH), '%Y-%m-01')
                    AND tct.trial_datetime < DATE_FORMAT(DATE_ADD(CURRENT_DATE(), INTERVAL 1 MONTH), '%Y-%m-01')
                GROUP BY 
                    DATE_FORMAT(tct.trial_datetime, '%Y-%m') 
                ORDER BY 
                    month 
                LIMIT 12;
            '''
            cursor.execute(sql)
            res=cursor.fetchall() 
            if res:
                cols=['month','trial_qty','dealed_qty','dealed_rate']
                result['trial_and_dealed_by_month']=self.res_to_dic(res,cols)
            else:
                result['trial_and_dealed_by_month']=None

            return jsonify({'res':'ok','data':result})
            
        except Exception as e:
            print('get income by type data error:',e)
            return jsonify({'res':'failed','error':str(e)})

    def get_cus_contribute_data(self):
        data=request.json
        interval=data['interval']
        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            if interval=='all':
                sql='''
                    SELECT cus_id, cus_name, SUM(real_pay) AS total_pay, COUNT(card_id) AS total_buy_qty, 
                        (SELECT SUM(real_pay) FROM buy_rec_table) AS all_total_real_pay, 
                        COALESCE(SUM(real_pay) / (SELECT SUM(real_pay) FROM buy_rec_table), 0) AS contribute_rate 
                    FROM buy_rec_table 
                    GROUP BY cus_id, cus_name 
                    ORDER BY total_pay DESC
                    LIMIT 10;
                '''
                cursor.execute(sql) 
            elif interval=='this_year':
                sql='''
                    SELECT cus_id, cus_name, SUM(real_pay) AS total_pay, COUNT(card_id) AS total_buy_qty, 
                        (SELECT SUM(real_pay) FROM buy_rec_table WHERE buy_date BETWEEN concat(year(curdate()),'-01-01') AND curdate()) AS all_total_real_pay, 
                        COALESCE(SUM(real_pay) / (SELECT SUM(real_pay) FROM buy_rec_table WHERE buy_date BETWEEN concat(year(curdate()),'-01-01') AND curdate()), 0) AS contribute_rate 
                    FROM buy_rec_table 
                    WHERE buy_date BETWEEN concat(year(curdate()),'-01-01') AND curdate()
                    GROUP BY cus_id, cus_name 
                    ORDER BY total_pay DESC
                    LIMIT 10;                 
                '''       
                cursor.execute(sql) 
            elif interval=='this_month':
                sql='''
                    SELECT cus_id, cus_name, SUM(real_pay) AS total_pay, COUNT(card_id) AS total_buy_qty, 
                        (SELECT SUM(real_pay) FROM buy_rec_table WHERE buy_date BETWEEN concat(year(curdate()),'-',month(curdate()),'-01') AND curdate()) AS all_total_real_pay, 
                        COALESCE(SUM(real_pay) / (SELECT SUM(real_pay) FROM buy_rec_table WHERE buy_date BETWEEN concat(year(curdate()),'-',month(curdate()),'-01') AND curdate()), 0) AS contribute_rate 
                    FROM buy_rec_table 
                    WHERE buy_date BETWEEN concat(year(curdate()),'-',month(curdate()),'-01') AND curdate()
                    GROUP BY cus_id, cus_name 
                    ORDER BY total_pay DESC
                    LIMIT 10;          
                '''       
                cursor.execute(sql) 
            else:
                sql='''
                    SELECT cus_id, cus_name, SUM(real_pay) AS total_pay, COUNT(card_id) AS total_buy_qty, 
                        (SELECT SUM(real_pay) FROM buy_rec_table WHERE buy_date BETWEEN date_sub(curdate(),INTERVAL %s-1 DAY) AND curdate() ) AS all_total_real_pay, 
                        COALESCE(SUM(real_pay) / (SELECT SUM(real_pay) FROM buy_rec_table WHERE buy_date BETWEEN date_sub(curdate(),INTERVAL %s-1 DAY) AND curdate()) , 0) AS contribute_rate 
                    FROM buy_rec_table 
                    WHERE buy_date BETWEEN date_sub(curdate(),INTERVAL %s-1 DAY) AND curdate() 
                    GROUP BY cus_id, cus_name 
                    ORDER BY total_pay DESC
                    LIMIT 10;                
                '''
                cursor.execute(sql,(interval,interval,interval))
            res=cursor.fetchall()
            cols=['cus_id','cus_name','total_pay','total_buy_qty','all_total_real_pay','contribute_rate']
            result=self.res_to_dic(res,cols)
            if res:
                return jsonify({'res':'ok','data':result})            
            else:
                return jsonify({'res':'failed','error':'no cus contribute data'})         
        except Exception as e:
            print('get income by type data error:',e)
            return jsonify({'res':'failed','error':str(e)})

    def get_income_stat_by_type(self):
        data=request.json
        interval=data['interval']
        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            if interval=='all':
                sql='''
                    SELECT 
                        brt.cashier_id as cashier_id,
                        it.ins_id as ins_id,
                        it.ins_name as ins_name,
                        brt.income_type as income_type,
                        COUNT(DISTINCT brt.card_id) AS buy_qty,
                        sum(brt.real_pay) as real_pay
                    FROM 
                        buy_rec_table brt
                    left JOIN ins_table it on it.related_cashier_id=brt.cashier_id
                    GROUP BY 
                        brt.cashier_id,brt.income_type,it.ins_id,it.ins_name;                
                '''
                cursor.execute(sql) 
            elif interval=='this_year':
                sql='''
                    SELECT 
                        brt.cashier_id as cashier_id,
                        it.ins_id as ins_id,
                        it.ins_name as ins_name,
                        brt.income_type as income_type,
                        COUNT(DISTINCT brt.card_id) AS buy_qty,
                        sum(brt.real_pay) as real_pay
                    FROM 
                        buy_rec_table brt
                    left JOIN ins_table it on it.related_cashier_id=brt.cashier_id
                    where buy_date BETWEEN concat(year(curdate()),'-01-01') AND curdate()
                    GROUP BY 
                        brt.cashier_id,brt.income_type,it.ins_id,it.ins_name;                
                '''       
                cursor.execute(sql) 
            elif interval=='this_month':
                sql='''
                    SELECT 
                        brt.cashier_id as cashier_id,
                        it.ins_id as ins_id,
                        it.ins_name as ins_name,
                        brt.income_type as income_type,
                        COUNT(DISTINCT brt.card_id) AS buy_qty,
                        sum(brt.real_pay) as real_pay
                    FROM 
                        buy_rec_table brt
                    left JOIN ins_table it on it.related_cashier_id=brt.cashier_id
                    where buy_date BETWEEN concat(year(curdate()),'-',month(curdate()),'-01') AND curdate()
                    GROUP BY 
                        brt.cashier_id,brt.income_type,it.ins_id,it.ins_name;                
                '''       
                cursor.execute(sql) 
            else:
                sql='''
                    SELECT 
                        brt.cashier_id as cashier_id,
                        it.ins_id as ins_id,
                        it.ins_name as ins_name,
                        brt.income_type as income_type,
                        COUNT(DISTINCT brt.card_id) AS buy_qty,
                        sum(brt.real_pay) as real_pay
                    FROM 
                        buy_rec_table brt
                    left JOIN ins_table it on it.related_cashier_id=brt.cashier_id
                    where buy_date BETWEEN date_sub(curdate(),INTERVAL %s-1 DAY) AND curdate() 
                    GROUP BY 
                        brt.cashier_id,brt.income_type,it.ins_id,it.ins_name;                
                '''
                cursor.execute(sql,interval)
            res=cursor.fetchall()
            cols=['cashier_id','ins_id','ins_name','income_type','buy_qty','real_pay']
            result=self.res_to_dic(res,cols)
            if res:
                return jsonify({'res':'ok','data':result})            
            else:
                return jsonify({'res':'failed','error':'no income by type data'})         
        except Exception as e:
            print('get income by type data error:',e)
            return jsonify({'res':'failed','error':str(e)})
        
    def get_vc_cfg(self):
        vc_cfg_fn=os.path.join(os.path.join(os.path.dirname(__file__),'config','vc_page_cfg'))
        # self.config_lz=readconfig.exp_json2(config_fn)
        with open(vc_cfg_fn,'r',encoding='utf-8') as f:
            vc_cfg=json.load(f)

        vc_txt_fn=os.path.join(os.path.join(os.path.dirname(__file__),'config','lz_txt_lib.xlsx'))
        df=pd.read_excel(vc_txt_fn,sheet_name='show_vc_page_txt')
        result = df.groupby(['类型'])['描述'].apply(list).groupby(level=0).apply(dict).to_dict()
        
        # 使用 groupby 函数进行分组并转换成字典
        grouped = df.groupby(['类型'])['描述'].apply(list).reset_index()
        result = {}
        for index, row in grouped.iterrows():           
            type_key = row['类型']
            description = row['描述']            
            result[type_key] = description

        return jsonify({'vc_cfg':vc_cfg,'vc_txt':result})
    
    def check_database_type(self):
        with open(os.path.join(os.path.dirname(os.path.realpath((__file__))),'config','db.config'),'r',encoding='utf-8') as f:
            cfg=json.load(f)
        if len(cfg['database'])>3 and cfg['database'][0:9]=='llz_trial':
            return jsonify({'data_base_type':'测试机','data_base_name':cfg['database']})
        else:
            return jsonify({'data_base_type':'生产机','data_base_name':cfg['database']})

    @login_required
    def set_vc_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./set_vc.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)
        
    @login_required
    def show_vc_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_vc.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)
   
    def get_cus_vc_list(self):
        data=request.json
        cus_id,cus_name=data['cusIdName'][:7],data['cusIdName'][7:]
        today=data['today']
        conn=self.connect_mysql()
        cursor=conn.cursor()

        try:
            sql='''
            SELECT 
                vct.vc_id,
                vct.vc_type,
                vct.vc_name,
                vct.amount,
                vct.start_date,
                vct.end_date,
                vct.vc_text,
                vct.use_for,
                CASE 
                    WHEN %s NOT BETWEEN vct.start_date AND vct.end_date THEN 'no'
                    ELSE 'yes'
                END AS valid,
                '-' AS use_cus_id, 
                '-' AS use_cus_name,
                cit.cus_id as cus_id,
                cit.cus_name as cus_name,
                cit.sex as sex
            FROM vc_cards_table vct
            LEFT JOIN vc_use_rec_table vurt ON vct.vc_id = vurt.vc_id
            LEFT JOIN vc_cardholder_table vcht ON vcht.vc_id = vct.vc_id
            LEFT JOIN cus_info_table cit ON cit.cus_id=vcht.cus_id
            WHERE vurt.vc_id IS NULL AND vcht.cus_id = %s

            UNION

            SELECT 
                vct.vc_id,
                vct.vc_type,
                vct.vc_name,
                vct.amount,
                vct.start_date,
                vct.end_date,
                vct.vc_text,
                vct.use_for,
                CASE 
                    WHEN %s NOT BETWEEN vct.start_date AND vct.end_date THEN 'no'
                    ELSE 'yes'
                END AS valid,
                '-' AS use_cus_id, 
                '-' AS use_cus_name,
                (select cus_id from cus_info_table where cus_id=%s) AS cus_id, 
                (select cus_name from cus_info_table where cus_id=%s) AS cus_name, 
                (select sex from cus_info_table where cus_id=%s) AS sex
            FROM vc_cards_table vct
            LEFT JOIN vc_use_rec_table vurt ON vct.vc_id = vurt.vc_id
            LEFT JOIN vc_cardholder_table vcht ON vcht.vc_id = vct.vc_id
            WHERE vcht.cus_id = 'all_cus'

            UNION

            SELECT 
                vct.vc_id,
                vct.vc_type,
                vct.vc_name,
                vct.amount,
                vct.start_date,
                vct.end_date,
                vct.vc_text,
                vct.use_for,
                CASE 
                    WHEN %s NOT BETWEEN vct.start_date AND vct.end_date THEN 'no'
                    ELSE 'no'
                END AS valid,
                vurt.cus_id AS use_cus_id,
                cit.cus_name AS use_cus_name,
                cit2.cus_id as cus_id,
                cit2.cus_name as cus_name,
                cit2.sex as sex
            FROM vc_use_rec_table vurt
            LEFT JOIN vc_cards_table vct ON vct.vc_id = vurt.vc_id
            LEFT JOIN vc_cardholder_table vcht ON vcht.vc_id = vct.vc_id
            LEFT JOIN cus_info_table cit ON vurt.cus_id = cit.cus_id
            LEFT JOIN cus_info_table cit2 ON vcht.cus_id = cit2.cus_id
            WHERE vcht.cus_id = %s
            
        '''
            cursor.execute(sql,(today,cus_id,today,cus_id,cus_id,cus_id,today,cus_id))
            res=cursor.fetchall()
            cols=['vc_id','vc_type','vc_name','amount','start_date','end_date','vc_text','use_for','valid','use_cus_id','use_cus_name','cus_id','cus_name','sex']
            result=self.res_to_dic(res,cols)
            if res:
                return jsonify({'res':'ok','data':result})
            
            else:
                return jsonify({'res':'failed','error':'no cus vc data'})
            

        except Exception as e:
            print('get cus vc data error:',e)
            return jsonify({'res':'failed','error':str(e)})
        

        finally:
            cursor.close()
            conn.close()

    def write_vc_use(self):
        data=request.json
        # print(data)
        vc_id=data['vcId']
        cus_id=data['cusId'][:7]
        buy_flow_id=data['buyFlowId']
        use_date=data['useDate']
        opr_id=data['oprId']
        opr_datetime=data['oprDatetime']
        conn=self.connect_mysql()
        cursor=conn.cursor()
        try:
            sql='''
                insert into vc_use_rec_table (vc_id,cus_id,buy_flow_id,use_date,opr_id,opr_datetime) values
                (%s,%s,%s,%s,%s,%s)
            '''
            cursor.execute(sql,(vc_id,cus_id,buy_flow_id,use_date,opr_id,opr_datetime))
            conn.commit()

            return jsonify({'res':'ok'})
        except Exception as e:
            print('write into vc_use_rec_table error:',e)
            return jsonify({'res':'failed','error':str(e)})
        finally:
            cursor.close()
            conn.close()

    def write_new_vc(self):
        data=request.json
        vc_id=data['vcId']
        cus_id=data['cusIdName'][:7]
        cus_name=data['cusIdName'][7:]
        vc_type=data['vcType']
        vc_name=data['vcName']
        amount=data['amount']
        start_date=data['startDate']
        end_date=data['endDate']
        vc_text=data['vcText']
        use_for=data['useFor']
        opr_id=data['oprId']
        produce_datetime=data['produceDatetime']

        conn=self.connect_mysql()
        cursor=conn.cursor()
        try:
            sql='''
                insert into vc_cards_table (vc_id,vc_type,vc_name,amount,start_date,end_date,vc_text,use_for,produce_datetime,opr_id) values
                (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
            '''
            cursor.execute(sql,(vc_id,vc_type,vc_name,amount,start_date,end_date,vc_text,use_for,produce_datetime,opr_id))

            sql='''
                insert into vc_cardholder_table (vc_id,cus_id,cus_name,related_datetime) values
                (%s,%s,%s,%s)
            '''
            cursor.execute(sql,(vc_id,cus_id,cus_name,produce_datetime))


            conn.commit()

            return jsonify({'res':'ok'})
        except Exception as e:
            print('write into vc cards or vc cardholder tables error:',e)
            return jsonify({'res':'failed','error':str(e)})
        finally:
            cursor.close()
            conn.close()


    def res_to_dic(self,res,cols):
        result=[]
        try:
            
            for item in res:
                dic={}
                for k,col in enumerate(cols):
                    dic[col]=item[k]
                result.append(dic)
            return result
        except Exception as e:
            print('res to dic error:',e)
            return
        

    def get_cus_birthday(self):
        print('get cus birthday data...')
        conn=self.connect_mysql()
        cursor=conn.cursor()
        try:
            sql='''
            select cit.cus_id,cit.cus_name,cit.birthday,icct.ins_ids,icct.ins_names from cus_info_table cit
            left join ins_control_cus_table icct on icct.cus_id=cit.cus_id
            order by cit.birthday;            
            '''
            cursor.execute(sql)
            res=cursor.fetchall()
            result=self.split_data(res)
            return jsonify({'res':'ok','data':result})
        except Exception as e:
            print('get cus birthday data error:',e)
            return jsonify({'res':'failed','error':str(e)})

        finally:
            cursor.close()
            conn.close()

    def split_data(self, results):
        split_results = []        
        for row in results:
            cus_id = row[0]
            cus_name = row[1]
            birthday = row[2]
            ins_ids = row[3]
            ins_names = row[4]
            
            if ins_ids and ins_names:
                ins_id_list = ins_ids.split(',')
                ins_name_list = ins_names.split(',')
                
                for ins_id, ins_name in zip(ins_id_list, ins_name_list):
                    split_results.append({
                        'cus_id': cus_id,
                        'cus_name': cus_name,
                        'birthday': birthday,
                        'ins_id': ins_id,
                        'ins_name': ins_name
                    })
            else:
                split_results.append({
                    'cus_id': cus_id,
                    'cus_name': cus_name,
                    'birthday': birthday,
                    'ins_id': ins_ids,
                    'ins_name': ins_names
                })

        # 按生日月日排序，忽略年     
        sorted_data = sorted(split_results, key=lambda x: (x['birthday'].month,x['birthday'].day))

        return sorted_data


    def deal_get_train_calendar(self):
        data=request.json
        interval=data['interval']
        cus_id_name=data['cusIdName']
        cus_id=cus_id_name[:7]
        result={}
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            if interval=='this_year':
                sql='''
                    select ctrt.cus_id, cit.sex,cus_name,cls_datetime, ti.merged_content from cls_tkn_rec_table ctrt
                    left join ( select cus_id,sex from cus_info_table) as cit on cit.cus_id= ctrt.cus_id
                    left join(
                        SELECT train_date, 
                        CONCAT_WS(';', 
                                IFNULL(GROUP_CONCAT(DISTINCT muscle SEPARATOR ';'), NULL), 
                                IFNULL(GROUP_CONCAT(DISTINCT SUBSTRING_INDEX(oxy_name, '-', -1) SEPARATOR ';'), NULL)
                                ) AS merged_content
                        FROM (
                            SELECT date(t.train_datetime) as train_date, t.muscle, NULL AS oxy_name
                            FROM train_nonoxy_rec_table t
                            WHERE t.cus_id=%s

                            UNION ALL

                            SELECT date(k.train_datetime) as train_date, NULL AS muscle, k.oxy_name
                            FROM train_oxy_rec_table k
                            WHERE k.cus_id=%s
                        ) AS combined_tables
                        GROUP BY train_date
                    ) as ti on ti.train_date=date(ctrt.cls_datetime)
                    where ctrt.cus_id=%s and date(cls_datetime) BETWEEN concat(year(curdate()),'-01-01') AND curdate()
                    order by cls_datetime;
                '''
                cursor.execute(sql,(cus_id,cus_id,cus_id))
            elif interval=='this_month':
                sql='''
                    select ctrt.cus_id, cit.sex,cus_name,cls_datetime, ti.merged_content from cls_tkn_rec_table ctrt
                    left join ( select cus_id,sex from cus_info_table) as cit on cit.cus_id= ctrt.cus_id
                    left join(
                        SELECT train_date, 
                        CONCAT_WS(';', 
                                IFNULL(GROUP_CONCAT(DISTINCT muscle SEPARATOR ';'), NULL), 
                                IFNULL(GROUP_CONCAT(DISTINCT SUBSTRING_INDEX(oxy_name, '-', -1) SEPARATOR ';'), NULL)
                                ) AS merged_content
                        FROM (
                            SELECT date(t.train_datetime) as train_date, t.muscle, NULL AS oxy_name
                            FROM train_nonoxy_rec_table t
                            WHERE t.cus_id=%s

                            UNION ALL

                            SELECT date(k.train_datetime) as train_date, NULL AS muscle, k.oxy_name
                            FROM train_oxy_rec_table k
                            WHERE k.cus_id=%s
                        ) AS combined_tables
                        GROUP BY train_date
                    ) as ti on ti.train_date=date(ctrt.cls_datetime)
                    where ctrt.cus_id=%s and date(cls_datetime) BETWEEN concat(year(curdate()),'-',month(curdate()),'-01') AND curdate()
                    order by cls_datetime;
                '''
                cursor.execute(sql,(cus_id,cus_id,cus_id))
                
            else:
                sql='''
                    select ctrt.cus_id, cit.sex,cus_name, cls_datetime,ti.merged_content from cls_tkn_rec_table ctrt
                    left join ( select cus_id,sex from cus_info_table) as cit on cit.cus_id= ctrt.cus_id
                    left join(
                        SELECT train_date, 
                        CONCAT_WS(';', 
                                IFNULL(GROUP_CONCAT(DISTINCT muscle SEPARATOR ';'), NULL), 
                                IFNULL(GROUP_CONCAT(DISTINCT SUBSTRING_INDEX(oxy_name, '-', -1) SEPARATOR ';'), NULL)
                                ) AS merged_content
                        FROM (
                            SELECT date(t.train_datetime) as train_date, t.muscle, NULL AS oxy_name
                            FROM train_nonoxy_rec_table t
                            WHERE t.cus_id=%s

                            UNION ALL

                            SELECT date(k.train_datetime) as train_date, NULL AS muscle, k.oxy_name
                            FROM train_oxy_rec_table k
                            WHERE k.cus_id=%s
                        ) AS combined_tables
                        GROUP BY train_date
                    ) as ti on ti.train_date=date(ctrt.cls_datetime)
                    where ctrt.cus_id=%s and date(cls_datetime) BETWEEN date_sub(curdate(),INTERVAL %s DAY) AND curdate()
                    order by cls_datetime;
                    '''
                cursor.execute(sql,(cus_id,cus_id,cus_id,interval))
            res=cursor.fetchall()
            if res:
                cursor.close()
                conn.close()
                return jsonify({'res':'ok','data':res})
            else:
                cursor.close()
                conn.close()
                return jsonify({'res':'failed','error':'no train date data'})
        
        except Exception as e:
            print('get ins cls info error:',e)
            cursor.close()
            conn.close()
            return jsonify({'res':'failed','error':str(e)})

    def deal_get_income_calendar_data(self):
        data=request.json
        interval=data['interval']
        result={}
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            if interval=='this_year':
                sql='''
                    select buy_date,sum(real_pay) as income from buy_rec_table 
                    where buy_date BETWEEN concat(year(curdate()),'-01-01') AND curdate()
                    group by buy_date 
                    having income>0 
                    order by buy_date;
                '''
                cursor.execute(sql)
            elif interval=='this_month':
                sql='''
                    select buy_date,sum(real_pay) as income from buy_rec_table 
                    where buy_date BETWEEN concat(year(curdate()),'-',month(curdate()),'-01') AND curdate()
                    group by buy_date 
                    having income>0 
                    order by buy_date;
                '''
                cursor.execute(sql)
            else:
                sql='''
                    select buy_date,sum(real_pay) as income from buy_rec_table 
                    where buy_date BETWEEN date_sub(curdate(),INTERVAL %s-1 DAY) AND curdate() 
                    group by buy_date 
                    having income>0 
                    order by buy_date;
                    '''
                cursor.execute(sql,interval)
            res=cursor.fetchall()
            if res:
                result['income_calendar_stat']=res
            else:
                result['income_calendar_stat']=''

            cursor.close()
            conn.close()
            if result:
                return jsonify({'res':'ok','data':result})
            else:
                return jsonify({'res':'failed','error':'no data'})
        except Exception as e:
            print('get cus info data error:',e)
            cursor.close()
            conn.close()
            return jsonify({'res':'failed','error':e})

    def deal_get_all_cus_stat(self):
        result={}
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            sql='''
                select count(*) as cus_qty, '合计' as sex from cus_info_table
                union
                select count(*) as cus_qty, sex from cus_info_table group by sex order by cus_qty desc;

            '''
            cursor.execute(sql)
            res=cursor.fetchall()
            result['cus_sex']=res


            sql='''
                SELECT 
                    age_ranges.age_group,
                    IFNULL(COUNT_AGE, 0) AS count,
                    IFNULL(COUNT_AGE / total_count , 0) AS percentage
                FROM (
                    SELECT '18岁以下' AS age_group
                    UNION ALL
                    SELECT '18-24岁'
                    UNION ALL
                    SELECT '25-29岁'
                    UNION ALL
                    SELECT '30-39岁'
                    UNION ALL
                    SELECT '40-49岁'
                    UNION ALL
                    SELECT '50岁及以上'
                ) AS age_ranges
                LEFT JOIN (
                    SELECT 
                        CASE 
                            WHEN TIMESTAMPDIFF(YEAR, birthday, CURDATE()) < 18 THEN '18岁以下'
                            WHEN TIMESTAMPDIFF(YEAR, birthday, CURDATE()) BETWEEN 18 AND 24 THEN '18-24岁'
                    		WHEN TIMESTAMPDIFF(YEAR, birthday, CURDATE()) BETWEEN 25 AND 29 THEN '25-29岁'
                            WHEN TIMESTAMPDIFF(YEAR, birthday, CURDATE()) BETWEEN 30 AND 39 THEN '30-39岁'
                            WHEN TIMESTAMPDIFF(YEAR, birthday, CURDATE()) BETWEEN 40 AND 49 THEN '40-49岁'
                            ELSE '50岁及以上'
                        END AS age_group,
                        COUNT(*) AS count_age
                    FROM cus_info_table
                    GROUP BY 
                        CASE 
                            WHEN TIMESTAMPDIFF(YEAR, birthday, CURDATE()) < 18 THEN '18岁以下'
                            WHEN TIMESTAMPDIFF(YEAR, birthday, CURDATE()) BETWEEN 18 AND 24 THEN '18-24岁'
                    		WHEN TIMESTAMPDIFF(YEAR, birthday, CURDATE()) BETWEEN 25 AND 29 THEN '25-29岁'
                            WHEN TIMESTAMPDIFF(YEAR, birthday, CURDATE()) BETWEEN 30 AND 39 THEN '30-39岁'
                            WHEN TIMESTAMPDIFF(YEAR, birthday, CURDATE()) BETWEEN 40 AND 49 THEN '40-49岁'
                            ELSE '50岁及以上'
                        END
                ) AS age_counts ON age_ranges.age_group = age_counts.age_group
                CROSS JOIN (
                    SELECT COUNT(*) AS total_count
                    FROM cus_info_table
                ) AS total_counts;

            '''
            cursor.execute(sql)
            res=cursor.fetchall()
            result['cus_stat']=res

            sql='''
                SELECT 
                    industry_table.industry_name,
                    COUNT(cus_info_table.industry_id) AS industry_count,
                    (COUNT(cus_info_table.industry_id) / (SELECT COUNT(*) FROM cus_info_table))  AS industry_percentage,
                    industry_table.industry_id
                FROM industry_table
                LEFT JOIN cus_info_table ON industry_table.industry_id = cus_info_table.industry_id
                GROUP BY industry_table.industry_name,industry_table.industry_id
                ORDER BY industry_table.industry_id;
            '''
            cursor.execute(sql)
            res=cursor.fetchall()
            result['industry_stat']=res


            sql='''
                SELECT 
                    occu_table.occu_name,
                    COUNT(cus_info_table.occu_id) AS occu_count,
                    (COUNT(cus_info_table.occu_id) /(SELECT COUNT(*) FROM cus_info_table)) AS occu_percentage,
                    occu_table.occu_id
                FROM occu_table
                LEFT JOIN cus_info_table ON occu_table.occu_id = cus_info_table.occu_id
                GROUP BY occu_table.occu_name,occu_table.occu_id
                ORDER BY occu_table.occu_id;
            '''
            cursor.execute(sql)
            res=cursor.fetchall()
            result['occu_stat']=res

            sql='''
                SELECT 
                    source, 
                    src_qty, 
                    src_qty / total_count AS percentage
                FROM 
                    (SELECT 
                        source, 
                        COUNT(*) AS src_qty
                    FROM 
                        cus_info_table
                    GROUP BY 
                        source) AS subquery, 
                    (SELECT 
                        COUNT(*) AS total_count 
                    FROM 
                        cus_info_table) AS total;

            '''
            cursor.execute(sql)
            res=cursor.fetchall()
            result['src_stat']=res
          
            if result:
                return jsonify({'res':'ok','data':result})
            else:
                return jsonify({'res':'failed','error':'no data'})
        except Exception as e:
            print('get cus info data error:',e)
            cursor.close()
            conn.close()
            return jsonify({'res':'failed','error':e})

    def deal_get_all_cls_info(self):
        data=request.json
        interval=data['interval']
        date_input=data['dateInput']
        result={}
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            sql='''
                SELECT ctrt.cus_id, cit.cus_name, COALESCE(sum(ctrt.cls_long),0) AS cls_qty, COALESCE(COUNT(ctrt.cls_datetime),0)*7/%s AS frqcy
                FROM cls_tkn_rec_table ctrt
                LEFT JOIN cus_info_table cit ON cit.cus_id = ctrt.cus_id
                WHERE date(cls_datetime) BETWEEN DATE_SUB(%s, INTERVAL %s DAY) AND %s
                GROUP BY ctrt.cus_id
                ORDER BY cls_qty DESC;
                '''
            cursor.execute(sql,(interval,date_input,interval,date_input))
            res=cursor.fetchall()
            if res:
                result['all_cls']=res
            else:
                result['all_cls']=None
            

            sql='''
                SELECT ctrt.ins_id,it.ins_name,COALESCE(SUM(ctrt.cls_long),0) AS cls_qty, COALESCE(COUNT(ctrt.cls_datetime),0)/%s AS frqcy
                FROM cls_tkn_rec_table ctrt
                LEFT JOIN ins_table it ON it.ins_id=ctrt.ins_id
                WHERE date(cls_datetime) BETWEEN DATE_SUB(%s, INTERVAL %s DAY) AND %s
                GROUP BY ctrt.ins_id,it.ins_name
                ORDER BY ctrt.ins_id;
                '''
            cursor.execute(sql,(interval,date_input,interval,date_input))
            res=cursor.fetchall()
            if res:
                result['cls_by_ins']=res
            else:
                result['cls_by_ins']=None

            return jsonify({'res':'ok','data':result})
        
        except Exception as e:
            print('get ins cls info error:',e)
            cursor.close()
            conn.close()
            return jsonify({'res':'failed','error':e})
        
    @login_required
    def upload_cus_photo_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./upload_cus_photo.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)
    
    @login_required
    def show_cus_calendar_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_cus_calendar.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def show_cus_birthday_list_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_cus_birthday_list.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def show_this_due_cus_list_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_this_due_cus_list.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)


    def get_this_month_due_data(self):
        conn=self.connect_mysql()
        cursor=conn.cursor()
        try:
            sql='''
                SELECT 
                    cct.ins_ids,
                    cct.ins_names,
                    cct.cus_id,
                    cit.cus_name,  -- 添加 cus_name 字段
                    ct.card_id,    
                    ct.max_end_time,
                    brt_ctrt.buy_num,
                    brt_ctrt.tkn_qty
                FROM (
                    SELECT 
                        card_id,
                        MAX(end_time) AS max_end_time
                    FROM cards_table
                    group by card_id
                ) AS ct
                LEFT JOIN (
                    SELECT DISTINCT icct.ins_ids,
                                    icct.ins_names,
                                    chc.cus_id,
                                    chc.card_id
                    FROM cardholder_card_table chc
                    LEFT JOIN (
                        SELECT icc.ins_ids,
                            icc.ins_names,
                            icc.cus_id
                        FROM ins_control_cus_table icc
                    ) AS icct ON chc.cus_id = icct.cus_id
                ) AS cct ON cct.card_id = ct.card_id
                LEFT JOIN cus_info_table cit ON cct.cus_id = cit.cus_id 
                -- 添加对 cus_info_table 的连接
                LEFT JOIN (
                    SELECT 
                        brt1.card_id AS card_id,
                        brt1.buy_num AS buy_num, 
                        ctrt1.tkn_qty AS tkn_qty 
                    FROM buy_rec_table brt1
                    LEFT JOIN (
                        SELECT 
                            card_id, 
                            sum(cls_long) AS tkn_qty 
                        FROM cls_tkn_rec_table 
                        GROUP BY card_id
                    ) AS ctrt1 ON ctrt1.card_id = brt1.card_id
                ) AS brt_ctrt ON brt_ctrt.card_id = ct.card_id
                WHERE YEAR(ct.max_end_time) = YEAR(CURDATE())
                            AND MONTH(DATE_SUB(ct.max_end_time,INTERVAL 1 DAY)) = MONTH(CURDATE())
                            GROUP BY 
                                cct.ins_ids, 
                                cct.ins_names, 
                                cct.cus_id, 
                                cit.cus_name, 
                                ct.card_id, 
                                brt_ctrt.buy_num, 
                                brt_ctrt.tkn_qty
                            HAVING  brt_ctrt.buy_num>brt_ctrt.tkn_qty
                            ORDER BY ct.max_end_time, cct.cus_id;
        '''
            cursor.execute(sql)
            res=cursor.fetchall()
            if res:
                return jsonify({'res':'ok','data':res})
            else:
                return jsonify({'res':'failed','error':'no data'})

        except Exception as e:
            print('get this month due lmt data error:',e)
            return jsonify({'res':'failed','error':str(e)})
            
        finally:
            conn.close()
            cursor.close()
            
    @login_required
    def show_all_cls_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_cus_img.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)
    
    @login_required
    def show_cus_img_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_cus_img.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)
        
    @login_required
    def show_cus_info_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_cus_info.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def show_lmt15_info_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_lmt15_info.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    def deal_get_ins_cls_info(self):
        data=request.json
        date_input=data['dateInput']
        interval=data['interval']
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            if interval=='all':
                sql='''
                    select 
                        ins_id,
                        ins_name,
                        hour(cls_datetime) as hour_prd,
                        count(hour(cls_datetime)) as hour_prd_qty 
                    from cls_tkn_rec_table 
                    group by ins_id,ins_name,hour_prd order by ins_id;
                '''
                cursor.execute(sql)
            else:
                sql='''
                    select 
                        ins_id,
                        ins_name,
                        hour(cls_datetime) as hour_prd,
                        count(hour(cls_datetime)) as hour_prd_qty 
                    from cls_tkn_rec_table 
                    where date(cls_datetime) BETWEEN DATE_SUB(%s, INTERVAL %s DAY) AND %s
                    group by ins_id,ins_name,hour_prd order by ins_id;
                '''
                cursor.execute(sql,(date_input,interval,date_input))
            res=cursor.fetchall()
            if res:
                return jsonify({'res':'ok','data':res})
            else:
                cursor.close()
                conn.close()
                return jsonify({'res':'failed','error':'no data'})

        except Exception as e:
            print('get ins cls info error:',e)
            cursor.close()
            conn.close()
            return jsonify({'res':'failed','error':e})

    def deal_query_lmt_15(self):
        conn=self.connect_mysql()
        cursor=conn.cursor()
        #查询满足条件的30天15节课的记录，并筛选出 新人新单
        sql='''
        SELECT 
            cct.cus_id,
            cit.cus_name,
            brt.card_id,
            COALESCE(ctrt.cls_tkn_qty, 0) AS cls_tkn_qty,
            15 - COALESCE(ctrt.cls_tkn_qty, 0) AS cls_remain_qty,
            ct.start_time,
            ct.end_time,
            icct.ins_ids,
            icct.ins_names,
            brt.comment,
            brt.buy_date
        FROM 
            buy_rec_table brt
        LEFT JOIN (
            SELECT cus_id, card_id 
            FROM cardholder_card_table
        ) AS cct ON cct.card_id = brt.card_id
        LEFT JOIN (
            SELECT cus_id, cus_name 
            FROM cus_info_table
        ) AS cit ON cit.cus_id = cct.cus_id
        LEFT JOIN (
            SELECT sum(cls_long) AS cls_tkn_qty, card_id 
            FROM cls_tkn_rec_table 
            GROUP BY card_id 
        ) AS ctrt ON ctrt.card_id = brt.card_id
        LEFT JOIN (
            SELECT ins_ids, ins_names, cus_id 
            FROM ins_control_cus_table
        ) AS icct ON icct.cus_id = cct.cus_id
        LEFT JOIN (
            SELECT MAX(card_start_time) AS start_time, MAX(end_time) AS end_time, card_id 
            FROM cards_table 
            GROUP BY card_id
        ) AS ct ON ct.card_id = brt.card_id
        INNER JOIN (
            SELECT cus_id, MIN(buy_date) AS min_buy_date
            FROM buy_rec_table
            GROUP BY cus_id
        ) AS min_brt ON min_brt.cus_id = cct.cus_id AND min_brt.min_buy_date = brt.buy_date
        WHERE 
            brt.buy_type = '限时课程'
            AND brt.buy_num = 15
            AND brt.buy_cls_days = 30
            AND ct.start_time IS NOT NULL
            AND COALESCE(ctrt.cls_tkn_qty,0) >= 0
            AND 15 - COALESCE(ctrt.cls_tkn_qty, 0)>0     
            AND %s BETWEEN ct.start_time AND DATE_SUB(ct.end_time,INTERVAL 1 DAY)
        ORDER BY ct.end_time;

    '''
        
        try:
            date=datetime.datetime.now().strftime('%Y-%m-%d')
            cursor.execute(sql,date)
            res=cursor.fetchall()
            cols=('cus_id','cus_name','card_id','cls_tkn_qty','cls_remain_qty','start_time','end_time','ins_ids','ins_names','cmt','buy_date')
            if res:
                dic=self.res_to_dic(res,cols=cols)
                return jsonify({'res':'ok','data':res,'data_dic':dic,'cols':cols})
            else:
                print('error','no lmt 15 data')
                return jsonify({'res':'failed','error':'no lmt 15 data'})
        except Exception as e:
            print('get lmt 15 error:',e)
            return jsonify({'res':'failed','error':e})

    @login_required
    def show_cus_train_bydate_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_cus_train_bydate.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)
    
    def deal_query_cus_train_item_bydate(self):        
        data=request.json
        cus_id=data['cusIdName'][:7]
        cus_name=data['cusIdName'][7:]
        s_date=data['startDate']
        e_date=data['endDate']
        try:
            sql='''
            SELECT cus_id, cus_name, GROUP_CONCAT(DISTINCT oxy_name SEPARATOR ',') AS oxy_names, 'oxy' AS train_type, DATE(train_datetime) AS train_date
            FROM train_oxy_rec_table
            WHERE cus_id = %s AND DATE(train_datetime) BETWEEN %s AND %s
            GROUP BY cus_id, cus_name, train_type, train_date
            union
            SELECT cus_id, cus_name, GROUP_CONCAT(DISTINCT non_oxy_name SEPARATOR ',') AS non_oxy_names, 'non_oxy' AS train_type, DATE(train_datetime) AS train_date
            FROM train_nonoxy_rec_table
            WHERE cus_id = %s AND DATE(train_datetime) BETWEEN %s AND %s
            GROUP BY cus_id, cus_name, train_type, train_date
            order by train_date desc;            
            '''
            conn=self.connect_mysql()
            cursor=conn.cursor()
            cursor.execute(sql,(cus_id,s_date,e_date,cus_id,s_date,e_date))
            res=cursor.fetchall()
            if res:
                cursor.close()
                conn.close()
                return jsonify({'res':'ok','data':res})            
            else:
                print('cus train item bydate no data')
                cursor.close()
                conn.close()
                return jsonify({'res':'failed','error':'no data'})
        
        except Exception as e:
            print('query cus train item by date error:',e)
            cursor.close()
            conn.close()
            return jsonify({'res':'failed','error':e})

    def get_latest_cus_train_days(self):
        print('get latest cus train days')
        data=request.json
        ins_id='%'+data['insId']+'%'
        today=datetime.datetime.now().strftime('%Y-%m-%d')
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            sql='''
                SELECT cit.cus_id, cus_name,COALESCE(date(ctt.max_tkn_date),'-') as max_tkn_date,
                    CASE
                        WHEN ctt.max_tkn_date IS NOT NULL
                        THEN DATEDIFF(%s, DATE(ctt.max_tkn_date))
                        ELSE '-'
                    END AS day_difference
                FROM cus_info_table cit
                LEFT JOIN (
                    SELECT cus_id, MAX(cls_datetime) AS max_tkn_date
                    FROM cls_tkn_rec_table
                    GROUP BY cus_id
                ) AS ctt ON ctt.cus_id = cit.cus_id
                where cit.cus_id in (
                    select cus_id from ins_control_cus_table where ins_ids like %s
                );            
            '''
            cursor.execute(sql,(today,ins_id))
            res=cursor.fetchall()

            sql='''select ins_name from ins_table where ins_id=%s'''
            cursor.execute(sql,data['insId'])
            res_ins_name=cursor.fetchone()
            if res:
                cursor.close()
                conn.close()
                return jsonify({'res':'ok','data':res,'ins_name':res_ins_name})
            else:
                cursor.close()
                conn.close()
                return jsonify({'res':'failed','data':'no data'})

        except Exception as e:
            print('get latest cus train days error:',e)
            cursor.close()
            conn.close()
            return jsonify({'res':'failed','error':e})

    @login_required    
    def show_cus_buyrec_lmtrec_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_cus_buyrec_lmtrec.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    def show_cus_buyrec_lmtrec(self):
        #查询的结果，当有重复的时候，保留实际改过的cls_qty的数量
        def filter_group(group):
            # 如果card_id有重复
            if len(group) > 1:
                # 选择符合 cls_qty < buy_num 的记录
                # filtered = group[(group['cls_qty'] < group['buy_num']) & (group['id'] == group['id'].max())]
                filtered = group[group['id'] == group['id'].max()]

                if not filtered.empty:
                    return filtered
            # 如果card_id没有重复，则直接返回该记录
            return group
        
        # def filter_group(group):
        #     if len(group) > 1:
        #         # 按 buy_date 降序排序，取第一条（最新记录）
        #         filtered = group.sort_values('buy_flow', ascending=False).head(1)
        #         return filtered
        #     return group
            
        data=request.json        
        cus_id_name=data['cusIdName'];
        cus_id=cus_id_name[:7]
        cus_name=cus_id_name[7:]
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            #new
            sql='''
                SELECT DISTINCT
    ctrt0.cus_id,
    cit.cus_name,
    brt.buy_date,
    cat.card_id,
    brt.buy_flow_id,
    brt.buy_type,
    brt.buy_cls_name,
    brt.buy_num,
    cat.cls_qty,
    cat.valid_time,
    brt.buy_cls_days,
    brt.pay,
    brt_sum.real_pay_sum AS real_pay,  -- 使用求和后的real_pay
    brt.cons_with_pay,
    brt.cashier_id,
    ct.cashier_name,
    cit.sex,
    COALESCE(cat.card_start_time, '-') AS start_time,
    COALESCE(cat.end_time, '-') AS end_time,
    COALESCE(ctrt.cls_tkn_qty, 0) AS cls_tkn_qty,
    (
        SELECT GROUP_CONCAT(DISTINCT CONCAT(cc.cus_id, cit2.cus_name)) 
        FROM cardholder_card_table cc
        JOIN cus_info_table cit2 ON cc.cus_id = cit2.cus_id
        WHERE cc.card_id = brt.card_id
    ) AS co_holder,
    COALESCE(all_vct.vc_id, '-') AS used_vc_id,
    COALESCE(all_vct.amount, 0) AS used_vc_amount,
    COALESCE(all_vct.use_for, '-') AS use_for,
    cat.cmt,
    cat.id
FROM 
    cards_table cat
LEFT JOIN
    cardholder_card_table ctrt0 
    ON ctrt0.card_id = cat.card_id
LEFT JOIN 
    (
        -- 子查询1：获取每个card_id的最新记录（id最大）
        SELECT 
            b1.*
        FROM 
            buy_rec_table b1
        JOIN (
            SELECT card_id, MAX(id) AS max_id
            FROM buy_rec_table
            GROUP BY card_id
        ) b2 ON b1.card_id = b2.card_id AND b1.id = b2.max_id
    ) AS brt 
    ON brt.card_id = cat.card_id
LEFT JOIN 
    (
        -- 子查询2：计算每个card_id的real_pay总和
        SELECT 
            card_id, 
            SUM(real_pay) AS real_pay_sum
        FROM 
            buy_rec_table
        GROUP BY 
            card_id
    ) AS brt_sum 
    ON brt_sum.card_id = brt.card_id
LEFT JOIN 
    (SELECT cashier_id, cashier_name FROM cashier_table) AS ct 
    ON ct.cashier_id = brt.cashier_id
LEFT JOIN 
    (SELECT cus_id, cus_name, sex FROM cus_info_table) AS cit 
    ON cit.cus_id = ctrt0.cus_id
LEFT JOIN 
    (SELECT COALESCE(sum(cls_long), 0) AS cls_tkn_qty, card_id 
     FROM cls_tkn_rec_table GROUP BY card_id) AS ctrt 
    ON ctrt.card_id = brt.card_id
LEFT JOIN
    (SELECT vurt.cus_id, vurt.buy_flow_id, vct.vc_id, vct.amount, vct.use_for 
     FROM vc_use_rec_table vurt 
     LEFT JOIN vc_cards_table vct ON vct.vc_id = vurt.vc_id) AS all_vct 
    ON all_vct.buy_flow_id = brt.buy_flow_id
WHERE 
    cat.card_id IN (
        SELECT card_id 
        FROM cardholder_card_table 
        WHERE cus_id = %s
    )
    AND cat.id IN (
        SELECT MAX(cat2.id) 
        FROM cards_table cat2 
        WHERE cat2.card_id = cat.card_id
    )
ORDER BY 
    brt.buy_date DESC;

            '''
            cursor.execute(sql,cus_id)
            res=cursor.fetchall()
            if res:
                en_cols=['cus_id','cus_name','buy_date','card_id','buy_flow_id','buy_type',
                                            'buy_cls_name','buy_num','cls_qty','valid_time','buy_cls_days','pay','real_pay','cons_with_pay',
                                            'cashier_id','cashier_name','sex','card_start_time','end_time','cls_tkn_qty',
                                            'co_holder','used_vc_id','used_vc_amount','use_for','cmt','id']
                df=pd.DataFrame(res,columns=en_cols)
                
                #用上面的去重逻辑filter_group进行card_id的去重
                filtered_df = df.groupby('card_id', group_keys=False).apply(filter_group)
                # print(df)
                # print(filtered_df)
                filtered_df.drop_duplicates(inplace=True)
                filtered_df.reset_index(inplace=True)
                
                # cols=('cus_id','cus_name','buy_date','card_id','buy_flow_id','buy_type','buy_cls_name','buy_num','cls_qty','buy_cls_days','pay','real_pay','cashier_id','cashier_name')
                cols=('客户ID','客户姓名','购课日期','卡号','购课流水','购课类型','课程名称','购课节数','实际节数','有效时段','课程天数','应付金额','实付金额','与应付一致',
                    '收款人ID','收款人姓名','性别','课程开始时间','课程结束时间','已上节数','共同持卡人','使用优惠券号','使用优惠券金额或折扣','用途','备注','id')
                # return jsonify({'res':'ok','data':res,'cols':cols})

                return jsonify({'res':'ok','data':self.dic_format(filtered_df,en_cols,transfer_int_64='yes'),'cols':cols})
                # return jsonify({'res':'ok','data':filtered_df.values.tolist(),'cols':cols})
            else:
                return jsonify({'res':'failed','error':'no buy rec data'})            
        except Exception as e:                  
            print('get cus buy history error:',e)
            return jsonify({'res':'failed','error':e})
        finally:
            cursor.close()
            conn.close()
  
    @login_required
    def show_ins_cls_stat_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_ins_cls_stat.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def group_class_input_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./group_class_input.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def cal_enddate_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./cal_enddate.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def cal_calory_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./cal_calory.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)
    
    def deal_calculate_calory(self):
        with open(os.path.join(os.path.dirname(os.path.realpath((__file__))),'config','cal_parameters.config'),'r',encoding='utf-8') as f:
            cfg=json.load(f)
        return jsonify({'res':'ok','data':cfg['calory_taken']['active_level']})

    @login_required
    def update_train_item_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./update_train_item.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    def deal_update_train_item(self):
        data=request.json
        print('updating train item table')
        form=data['form']
        category=data['category']
        muscle=data['muscle']
        action_name=data['action_name']
        opr_id=data['opr_id']
        opr_time=data['opr_time']
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            sql='''
                insert into train_item_table (form,muscle,category,action_name,opr_id,opr_time) values 
                (%s,%s,%s,%s,%s,%s)
            '''
            cursor.execute(sql,(form,muscle,category,action_name,opr_id,opr_time))
            conn.commit()
            cursor.close()
            conn.close()
            return jsonify({'res':'ok'})
        except Exception as e:
            cursor.close()
            conn.close()
            return jsonify({'res':'failed','err':e})

    def get_plan_history(self):
        data=request.json
        conn=self.connect_mysql()
        cursor=conn.cursor()
        try:
            sql='''select cus_id from train_plan_table where cus_id=%s'''
            cursor.execute(sql,data['cus_name'][:7])
            res=cursor.fetchall()
 
            if res:
                return jsonify({'res':'ok'})
            else:
                return jsonify({'res':'failed'})
        except Exception as e:
            return jsonify({'res':'failed'})

    def check_login(self):
        data=request.json
        conn=self.connect_mysql()
        cursor=conn.cursor()
        try:
            sql='''select ins_id,account_status from ins_table where ins_id=%s'''
            cursor.execute(sql,data['user_id'])
            res=cursor.fetchone()
            # print('check login:',res)
            if res and res[1]=='on':
                return jsonify({'res':'ok'})
            else:
                print(f"{data['user_id']} is not valid")
                return jsonify({'res':'failed'})

        except Exception as e:
             return jsonify({'res':'failed'})
    
    def upload_pic(self):
        file = request.files["file"]
        filename =  request.args.get("filename")
        # print(file,file.filename,filename)
        if not file.content_type.startswith("image/") or not file.filename.endswith((".jpg", ".jpeg", ".png")):
            return {"message": "没有选择文件 image"}, 400
        
        # file = request.files["image"]
        if file.filename == "":
            return {"message": "没有选择文件 empty"}, 400

        if file.content_type.split('/')[0] != 'image':
         # 文件不是图片类型，返回错误信息
            return {"message": "请上传图片文件"},400
        
        if file and self.allowed_file(filename):
            # filename = secure_filename(filename)
            print('checked filename:',filename)
            comp_pic_dir=self.config_lz['comp_pic_dir']    
            save_fn=os.path.join(comp_pic_dir, filename)       
            file.save(save_fn)
            img = Image.open(save_fn)
            quality=95
            target_size_kb=1300
            step=10
            while True:
                img.save(save_fn, quality=quality)
                #压缩图片                
                file_size_kb = os.path.getsize(save_fn) / 1024            
                if file_size_kb <= target_size_kb or quality == 0:
                    break
                quality -= step
                if quality < 0:
                    quality = 0
            return {"message": "上传成功"}
        else:
            return {"message": "只能上传图片"}, 400

    def allowed_file(self,filename):
        return "." in filename and filename.rsplit(".", 1)[1].lower() in {"jpg", "jpeg", "png", "gif"}

    def upload_cus_photo(self):
        file = request.files["file"]
        filename =  request.args.get("filename")
        # print(file,file.filename,filename)
        if not file.content_type.startswith("image/") or not file.filename.endswith((".jpg", ".jpeg", ".png")):
            return jsonify({"message": "没有选择文件 image"}), 400
        
        # file = request.files["image"]
        if file.filename == "":
            return jsonify({"message": "没有选择文件 empty"}), 400

        if file.content_type.split('/')[0] != 'image':
         # 文件不是图片类型，返回错误信息
            return jsonify({"message": "请上传图片文件"}),400
        
        if file and self.allowed_file(filename):
            # filename = secure_filename(filename)
            print('checked filename:',filename)
            cus_photo_dir=self.config_lz['cus_photo_dir']    
            save_fn=os.path.join(cus_photo_dir, filename)       
            file.save(save_fn)
            img = Image.open(save_fn)
            img=self.crop_3_4(img)
            quality=80
            target_size_kb=60
            step=10
            while True:
                img.save(save_fn, quality=quality)
                #压缩图片                
                file_size_kb = os.path.getsize(save_fn) / 1024            
                if file_size_kb <= target_size_kb or quality == 0:
                    break
                quality -= step
                if quality < 0:
                    quality = 0
            return jsonify({"message": "上传成功"})
        else:
            return jsonify({"message": "只能上传图片"}), 400

    def crop_3_4(self,img): 
        width, height = img.size

        # 计算 3:4 比例部分的尺寸
        new_width = min(width, int(height * 3 / 4))
        new_height = min(height, int(width * 4 / 3))

        left = (width - new_width) // 2
        top = (height - new_height) // 2
        right = left + new_width
        bottom = top + new_height

        # 裁剪图像
        cropped_img = img.crop((left, top, right, bottom))
        return cropped_img

    def get_train_plan_dates(self):
        data=request.json
        cus_id_name=data['cus_id_name']
        print(cus_id_name)
        cus_id=cus_id_name[:7]
        cus_name=cus_id_name[7:]
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            sql='''
                select distinct plan_date from train_plan_table 
                    inner join body_msr_table on body_msr_table.msr_date=train_plan_table.plan_date 
                where train_plan_table.cus_id=%s
                order by plan_date desc;
            '''
            cursor.execute(sql,cus_id)
            res=cursor.fetchall()
            # print(res)
            return {'res':'ok','plan_date_list':res}

        except Exception as e:
            print('get plan dates error:',e)
            return {'res':'failed'}
        finally:
            cursor.close()
            conn.close()

    def get_compare_image(self):
        url=request.json
        filename=url['url']
        # print('file name for compare img:',filename)
        return send_file(filename)
    
    def get_cus_image(self):
        data=request.json
        cus_id_name=data['cusIdName']
        cus_photo_dir=self.config_lz['cus_photo_dir']        
        filename=os.path.join(cus_photo_dir,'empty.jpg')
        for fn in os.listdir(cus_photo_dir):
            if fn==cus_id_name+'.jpg':
                filename=os.path.join(cus_photo_dir,cus_id_name+'.jpg')        
        return send_file(filename)

    def get_train_plan(self):
        data=request.json
        cus_id=data['cus_id_name'][:7]
        cus_name=data['cus_id_name'][7:]
        date_input=data['date']
        comp_pic_dir=self.config_lz['comp_pic_dir']+'\\'
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            sql='''
                SELECT
                    cit.sex,ht, wt, chest, l_arm,r_arm,waist, hip,
                    ROUND(waist/hip, 2) AS waist_hip_ratio,
                    l_leg, r_leg, l_calf, r_calf,
                    convert(ROUND(wt/POW(ht/100, 2), 2),CHAR) AS bmi,
                    bfr, body_problems, tt.cus_goals, tt.ins_suggests,
                    tt.stage, tt.stage_goal, tt.train_summary, CONCAT(%s,tt.comp_pic_url) as comp_pic_url
                    FROM
                    body_msr_table bt
                LEFT JOIN
                    (SELECT * FROM train_plan_table WHERE cus_id=%s AND plan_date=%s) AS tt
                    ON bt.msr_date = tt.plan_date
                LEFT JOIN
                    (SELECT cus_id,sex FROM cus_info_table WHERE cus_id=%s) AS cit
                    ON bt.cus_id = cit.cus_id
                WHERE
                bt.cus_id=%s AND bt.msr_date=%s;
            '''
            cursor.execute(sql,(comp_pic_dir,cus_id,date_input,cus_id,cus_id,date_input))
            res_plan=cursor.fetchone()

            col_names=['sex','ht','wt','chest','l_arm','r_arm','waist','hip','waist_hip_ratio','l_leg','r_leg','l_calf','r_calf','bmi','bfr','problems','cus_goals','ins_suggests','stage','stage_goal','train_summary','comp_pic_url']
            res_plan_dic={}
            for n,col_name in enumerate(col_names):
                res_plan_dic[col_name]=res_plan[n]

            sql='''
                SELECT
                train_purpose,train_part,train_frqcy,train_strength,train_benifit,train_type,train_prd,train_active
                FROM train_stage_detail_table
                WHERE
                cus_id=%s AND plan_date=%s;
            '''
            cursor.execute(sql,(cus_id,date_input))
            res_detail=cursor.fetchall()

            # print(res_plan, res_detail)

            if res_plan and res_detail:
                return jsonify({'res':'ok','body_and_plan':res_plan_dic,'stage_detail':res_detail})
            
            else:
                return jsonify({'res':'failed','error':'res plan or res detail empty'})

        except Exception as e:
            print('get train plan error:',e)
            return jsonify({'res':'failed','error':e})

        finally:
            cursor.close()
            conn.close()

    def get_page_txts(self):        
        xlsx=os.path.join(os.path.dirname(os.path.realpath((__file__))),'config','lz_txt_lib.xlsx')
        df=pd.read_excel(xlsx,sheet_name='show_body_page_txt')

        result = df.groupby(['页面', '类型'])['描述'].apply(list).groupby(level=0).apply(dict).to_dict()
        
        # 使用 groupby 函数进行分组并转换成字典
        grouped = df.groupby(['页面', '类型'])['描述'].apply(list).reset_index()
        result = {}
        for index, row in grouped.iterrows():
            page_type = row['页面']
            type_key = row['类型']
            description = row['描述']
            if page_type not in result:
                result[page_type] = {}
            result[page_type][type_key] = description
        return jsonify({'res':result})
    
    def get_plan_page_txts(self):
        xlsx=os.path.join(os.path.dirname(os.path.realpath((__file__))),'config','lz_txt_lib.xlsx')
        df=pd.read_excel(xlsx,sheet_name='show_plan_page_txt')

        result = df.groupby(['页面', '类型'])['描述'].apply(list).groupby(level=0).apply(dict).to_dict()
        
        # 使用 groupby 函数进行分组并转换成字典
        grouped = df.groupby(['页面', '类型'])['描述'].apply(list).reset_index()
        result = {}
        for index, row in grouped.iterrows():
            page_type = row['页面']
            type_key = row['类型']
            description = row['描述']
            if page_type not in result:
                result[page_type] = {}
            result[page_type][type_key] = description
        return jsonify({'res':result})

    def get_input_body_page_options(self):
        xlsx=os.path.join(os.path.dirname(os.path.realpath((__file__))),'config','lz_txt_lib.xlsx')
        df=pd.read_excel(xlsx,sheet_name='input_body_page_options')
        res_stage=df['所处阶段'].dropna().tolist()
        res_type=df['训练类型'].dropna().tolist()

        return jsonify({'res_stage':res_stage,'res_type':res_type})

    #客户训练报告
    def get_cus_report_train(self):
        data=request.json
        cus_id=data['cus_name_id'][:7]
        
        result={}


         #查询客户的性别、年龄
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            sql='''
                SELECT sex  FROM `cus_info_table` WHERE cus_id=%s
            '''
            cursor.execute(sql,cus_id)
            cus_sex=cursor.fetchone()[0]
            result['sex']=cus_sex
        except Exception as e:
            print('get cus sex, age error: ',e)
            cus_sex=None
        finally:
            cursor.close()
            conn.close()

        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()

            

             #查询客户训练第一日，最后一日，总天数
            sql='''
                select max(cls_datetime) as max_date, min(cls_datetime)as min_date,DATEDIFF(curdate(),min(cls_datetime))+1 AS days_diff from cls_tkn_rec_table where cus_id=%s;
            '''
            cursor.execute(sql,cus_id)
            train_date_max,train_date_min,train_days=cursor.fetchone()
            
            if train_date_min and train_date_max and train_days:
                result['train_date_max']=train_date_max
                result['train_date_min']=train_date_min
                result['total_club_days']=train_days
            else:
                return jsonify({'res':'failed'})

            #按月查询客户上课次数
            sql='''
                select MONTH(cls_datetime) as month,sum(cls_long) as cls_tkn_qty  from cls_tkn_rec_table where cus_id=%s GROUP by MONTH(cls_datetime)
            '''
            cursor.execute(sql,cus_id)
            cls_tkn_stat_by_month=cursor.fetchall()
            if cls_tkn_stat_by_month:
                result['cls_tkn_stat_by_month']=cls_tkn_stat_by_month
            else:
                return jsonify({'res':'failed'})

            #查询上课总次数
            sql='''
                select sum(cls_long) as cls_tkn_qty  from cls_tkn_rec_table where cus_id=%s
            '''
            cursor.execute(sql,cus_id)
            total_train_times=cursor.fetchone()[0]
            if total_train_times:
                result['total_train_times']=total_train_times
            else:
                return jsonify({'res':'failed'})



            #查询客户有氧总时间，有氧时长/周
            sql='''
            SELECT 
                IFNULL(b.b_total_oxy_time, 0) as total_oxy_time, 
                DATEDIFF(curdate(), MIN(c.cls_datetime)) AS days_diff,
                IFNULL(b.b_total_oxy_time*7/DATEDIFF(curdate(), MIN(c.cls_datetime)), 0) as oxy_time_per_week
            FROM cls_tkn_rec_table c
            JOIN 
                (SELECT cus_id, SUM(oxy_time) AS b_total_oxy_time 
                FROM train_oxy_rec_table 
                WHERE cus_id = %s and oxy_name NOT REGEXP '^[A-Z]-' and oxy_name <> "小团课训练"
                GROUP BY cus_id
                ) AS b
            ON c.cus_id = b.cus_id
            WHERE c.cus_id = %s;

            '''
            cursor.execute(sql,(cus_id,cus_id))
            res=cursor.fetchone()
            if res:
                result['total_oxy_time']=res[0]
                result['oxy_time_per_week']=res[2]
            else:
                result['total_oxy_time']=0
                result['oxy_time_per_week']=0

            #查询客户有氧总时间，有氧时长/周
            sql='''
            SELECT 
                IFNULL(b.b_total_oxy_time, 0) as total_oxy_time, 
                DATEDIFF(curdate(), MIN(c.cls_datetime)) AS days_diff,
                IFNULL(b.b_total_oxy_time*7/DATEDIFF(curdate(), MIN(c.cls_datetime)), 0) as oxy_time_per_week
            FROM cls_tkn_rec_table c
            JOIN 
                (SELECT cus_id, SUM(oxy_time) AS b_total_oxy_time 
                FROM train_oxy_rec_table 
                WHERE cus_id = %s and oxy_name NOT REGEXP '^[A-Z]-' and oxy_name <> "小团课训练"
                GROUP BY cus_id
                ) AS b
            ON c.cus_id = b.cus_id
            WHERE c.cus_id = %s;

            '''
            cursor.execute(sql,(cus_id,cus_id))
            res=cursor.fetchone()
            if res:
                result['total_oxy_time']=res[0]
                result['oxy_time_per_week']=res[2]
            else:
                result['total_oxy_time']=0
                result['oxy_time_per_week']=0

            #查询客户有小团课训练信息
            sql='''
            SELECT 
                IFNULL(b.b_total_oxy_time, 0) as total_oxy_time, 
                DATEDIFF(curdate(), MIN(c.cls_datetime)) AS days_diff,
                IFNULL(b.b_total_oxy_time*7/DATEDIFF(curdate(), MIN(c.cls_datetime)), 0) as oxy_time_per_week,
                count(c.cus_id) as total_group_train_times
            FROM cls_tkn_rec_table c
            JOIN 
                (SELECT cus_id, SUM(oxy_time) AS b_total_oxy_time 
                FROM train_oxy_rec_table 
                WHERE cus_id = %s and oxy_name NOT REGEXP '^[A-Z]-' and oxy_name = "小团课训练"
                GROUP BY cus_id
                ) AS b
            ON c.cus_id = b.cus_id
            WHERE c.cus_id = %s;

            '''
            cursor.execute(sql,(cus_id,cus_id))
            res=cursor.fetchone()
            if res:
                result['total_group_time']=res[0]
                result['group_time_per_week']=res[2]
                result['total_group_train_times']=res[3]
            else:
                result['total_group_time']=0
                result['group_time_per_week']=0
                result['total_group_train_times']=0

            #查询客户其他训练次数
            sql='''
            select 
                SUBSTRING_INDEX(oxy_name, '-', -1) as other_train_name,count(oxy_name) as qty
            from
                train_oxy_rec_table
            where 
                cus_id=%s
                AND
            oxy_name REGEXP '^[A-Z]-' and oxy_name <> "小团课训练"
            GROUP BY
                oxy_name
            '''
            cursor.execute(sql,cus_id)
            res=cursor.fetchall()
            if res:
                result['other_train']=res
            
            else:
                result['other_train']=None
                

            #按部位查询客户肌肉训练次数
            sql='''
                select muscle,count(train_datetime) as train_times from train_nonoxy_rec_table where cus_id=%s group by muscle
            '''
            cursor.execute(sql,cus_id)
            res=cursor.fetchall()
            if res:
                result['non_oxy_train_by_muscle']=res
            
            else:
                result['non_oxy_train_by_muscle']=None

            #查询客户肌肉训练天数/周
            sql='''
                SELECT 
                    muscle_times.total AS total_muscle_times,
                    DATEDIFF(CURDATE(), first_class_date.first_date)+1 AS days_diff,
                    (muscle_times.total * 7) / NULLIF(DATEDIFF(CURDATE(), first_class_date.first_date)+1, 0) AS total_muscle_times_per_week
                FROM 
                (SELECT 
                    COUNT(DISTINCT DATE(train_datetime)) AS total 
                FROM train_nonoxy_rec_table 
                WHERE cus_id = %s) AS muscle_times,
                (SELECT 
                    MIN(cls_datetime) AS first_date 
                FROM 
                    cls_tkn_rec_table 
                WHERE 
                    cus_id = %s) AS first_class_date;
            '''
            cursor.execute(sql,(cus_id,cus_id))
            res=cursor.fetchone()
            if res:
                result['total_muscle_days']=res[0]
                result['muscle_days_per_week']=res[2]
            else:
                result['total_muscle_days']=0
                result['muscle_days_per_week']=None
            



        except Exception as e:
            print('get train data error: ',e)
            return jsonify({'res':'failed'})
        finally:
            cursor.close()
            conn.close()

        return jsonify({'res':'ok','result':result})

    #客户身体情况报告
    def get_cus_report_body(self):
        with open(os.path.join(os.path.join(os.path.dirname(__file__),'config','bfr_rate.json')),'r',encoding='utf-8') as f:
            bfr_table=json.load(f)
        data=request.json
        cus_id=data['cus_name_id'][:7]
        result={}

        
        #查询客户的性别、年龄
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            sql='''
                SELECT sex,TIMESTAMPDIFF(YEAR, birthday, CURDATE()) AS age FROM `cus_info_table` WHERE cus_id=%s
            '''
            cursor.execute(sql,cus_id)
            cus_sex,cus_age=cursor.fetchone()
        except Exception as e:
            print('get cus sex, age error: ',e)
            cus_sex=cus_age=None
        finally:
            cursor.close()
            conn.close()
        
        if cus_age<40:
            cus_level='18-39'
        elif cus_age>=40 and cus_age<60:
            cus_level='40-59'
        elif cus_age>=60:
            cus_level='60-'
        
        #bfr阶梯划分及bfr，bmi,如果没有最近一期的bfr测量数据，则为None
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()

            sql='''
                SELECT msr_date,bfr,convert(round(wt/POW(ht/100,2),2),CHAR) as bmi from body_msr_table WHERE cus_id=%s order by msr_date desc limit 1
            '''
            cursor.execute(sql,cus_id)
            latest_bfr=cursor.fetchone()   

            if(latest_bfr):
                result['bfr_bmi']=bfr_table[cus_sex][cus_level]
                result['bfr_bmi']+=latest_bfr
            else:
                result['bfr_bmi']=None
                return jsonify({'res':'failed'})
        except Exception as e:
            print('get bfr,bmi error: ',e)
            result['bfr_bmi']=None
            return jsonify({'res':'failed'})
        finally:
            cursor.close()
            conn.close()

        #获取最近的身体素质五项
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()

            sql='''
                SELECT msr_date,balance,core,flex,heart,power from body_msr_table WHERE cus_id=%s order by msr_date desc limit 1
            '''
            cursor.execute(sql,cus_id)
            fiveItems=cursor.fetchone()   

            if(fiveItems):
                result['fiveItems']=fiveItems
            else:
                result['fiveItems']=None
                return jsonify({'res':'failed'})
        except Exception as e:
            print('get 5 body items error: ',e)
            result['fiveItems']=None
            return jsonify({'res':'failed'})
        finally:
            cursor.close()
            conn.close()

        #获取总共体测次数
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()

            sql='''
                SELECT count(msr_date) from body_msr_table WHERE cus_id=%s 
            '''
            cursor.execute(sql,cus_id)
            total_msr_times=cursor.fetchone()[0]

            if(total_msr_times):
                result['total_msr_times']=total_msr_times
            else:
                result['total_msr_times']=None
                return jsonify({'res':'failed'})
        except Exception as e:
            print('get total msr times error: ',e)
            result['total_msr_times']=None
            return jsonify({'res':'failed'})
        finally:
            cursor.close()
            conn.close()

        #获取最近5次围度数据
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()

            sql='''
                select msr_date,ht,wt,chest,waist,hip,l_arm,r_arm,l_leg,r_leg,l_calf,r_calf,COALESCE(body_problems,'-') AS body_problems 
                from body_msr_table 
                where cus_id=%s 
                order by msr_date asc limit 5;
            '''
            cursor.execute(sql,cus_id)
            msr_data=cursor.fetchall()   

            if msr_data:
                
                msr_date,wt,chest,waist,hip,l_arm,r_arm,l_leg,r_leg,l_calf,r_calf,body_problems=[],[],[],[],[],[],[],[],[],[],[],[]
                for dat in msr_data:
                    msr_date.append(dat[0])
                    wt.append(dat[2])
                    chest.append(dat[3])
                    waist.append(dat[4])
                    hip.append(dat[5])
                    l_arm.append(dat[6])
                    r_arm.append(dat[7])
                    l_leg.append(dat[8])
                    r_leg.append(dat[9])
                    l_calf.append(dat[10])
                    r_calf.append(dat[11])
                    body_problems.append(dat[12])
                result['latest5MsrData']={}
                result['latest5MsrData']['msr_date']=msr_date
                result['latest5MsrData']['wt']=wt
                result['latest5MsrData']['chest']=chest
                result['latest5MsrData']['waist']=waist
                result['latest5MsrData']['hip']=hip
                result['latest5MsrData']['l_arm']=l_arm
                result['latest5MsrData']['r_arm']=r_arm
                result['latest5MsrData']['l_leg']=l_leg
                result['latest5MsrData']['r_leg']=r_leg
                result['latest5MsrData']['l_calf']=l_calf
                result['latest5MsrData']['r_calf']=r_calf
                result['latest5MsrData']['body_problems']=body_problems
           

            else:
                result['latest5MsrData']=None
                return jsonify({'res':'failed'})

        except Exception as e:
            print('get 5 body items error: ',e)
            result['latest5MsrData']=None
            return jsonify({'res':'failed'})
        finally:
            cursor.close()
            conn.close()

        return jsonify({'res':'ok','result':result})
    
    @login_required
    def show_body_data(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_body_data.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def show_plan(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_plan.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    def get_ins_job_stat(self):
        data=request.json
        ins_id_name=data['insIdName']
        ins_id=ins_id_name[0:8]
        print('getting ins job data from llz_service...')
        result={}
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            #体验课出单统计
            sql='''
            SELECT 
                '-' as ins_id,
                '合计' as ins_name,
                COUNT(trial_cus_name) as total_count, 
                COALESCE(SUM(CASE WHEN deal_yn = '是' THEN 1 ELSE 0 END), 0) as dealed_qty,
                COALESCE(SUM(CASE WHEN deal_yn = '是' THEN 1 ELSE 0 END) / COUNT(trial_cus_name), 0) AS ratio
            FROM 
                trial_cls_table t
            UNION
            SELECT 
                it.ins_id,
                it.ins_name,
                COALESCE(COUNT(tct.trial_cus_name), 0) AS total_count,
                COALESCE(SUM(CASE WHEN tct.deal_yn = '是' THEN 1 ELSE 0 END), 0) AS dealed_qty,
                COALESCE(SUM(CASE WHEN tct.deal_yn = '是' THEN 1 ELSE 0 END) / NULLIF(COUNT(tct.trial_cus_name), 0), 0) AS ratio
            FROM 
                (SELECT ins_id, ins_name FROM ins_table WHERE ins_id = %s) AS it
            LEFT JOIN 
                trial_cls_table tct
            ON 
                it.ins_id = tct.ins_id AND month(tct.trial_datetime) = month(CURDATE()) and year(tct.trial_datetime) = year(curdate())
            GROUP BY 
                it.ins_id, it.ins_name;

            '''
            cursor.execute(sql,ins_id)
            res=cursor.fetchall()
            result['trial_dealed_stat']=res


            #业绩统计
            sql='''
                SELECT 
                    COALESCE(COUNT(br.buy_flow_id), 0) AS buy_qty, 
                    COALESCE(SUM(br.real_pay), 0) AS buy_realpay
                FROM 
                    (SELECT related_cashier_id FROM ins_table WHERE ins_id = %s) AS it
                LEFT JOIN 
                    buy_rec_table br
                ON 
                    it.related_cashier_id = br.cashier_id 
                    and year(br.buy_date) = year(curdate())
                    AND month(br.buy_date) = month(CURDATE())
                GROUP BY 
                    it.related_cashier_id;
            '''
            cursor.execute(sql,ins_id)
            res=cursor.fetchone()
            result['ins_sale_stat']=res

            if result:
                cursor.close()
                conn.close()
                return jsonify({'res':'ok','data':result})
            else:
                cursor.close()
                conn.close()
                return jsonify({'res':'failed','error':'no ins job stat data'})        
        except Exception as e:
            print('获取体验课数据错误：',e)
            cursor.close()
            conn.close()
            return jsonify({'res':'failed','error':str(e)})



    def get_business_data(self):
        print('getting businiess data from llz_service...')
        data=request.json
        s_date=data['start_date']
        e_date=data['end_date']

        conn=self.connect_mysql()
        cursor=conn.cursor()

        result={}
        try:
            #体验课清单
            sql='''
                SELECT trial_cls_table.ins_id, ifnull(ins_table.ins_name,'-'),date(trial_datetime) as trial_date,trial_cls_table.trial_cus_name,trial_cus_source,deal_yn  
                FROM trial_cls_table 
                LEFT JOIN ins_table AS ins_table ON ins_table.ins_id = trial_cls_table.ins_id
                WHERE DATE(trial_cls_table.trial_datetime) BETWEEN %s AND %s
                order by date(trial_datetime);
            '''


            cursor.execute(sql,(s_date,e_date))
            res=cursor.fetchall()
            result['trial_class_list']=res
            
            #体验课统计
            sql='''
                SELECT t.ins_id,ins.ins_name,count(t.trial_cus_name) as trial_qty from trial_cls_table t
                left JOIN
                (select ins_id,ins_name from ins_table) as ins
                on ins.ins_id=t.ins_id
                where date(t.trial_datetime) BETWEEN  %s AND %s
                group by t.ins_id,ins.ins_name;     
            '''

            cursor.execute(sql,(s_date,e_date))
            res=cursor.fetchall()
            result['trial_stat']=res


            #体验课出单统计
            sql='''
            SELECT 
                '-' as ins_id,
                '合计' as ins_name,
                COUNT(trial_cus_name) as total_count, 
                COALESCE(SUM(CASE WHEN  deal_yn= '是' THEN 1 ELSE 0 END),0) as dealed_qty,
                COALESCE(SUM(CASE WHEN  deal_yn= '是' THEN 1 ELSE 0 END) / COUNT(trial_cus_name),0) AS ratio
            FROM 
                trial_cls_table
            WHERE 
                DATE (trial_datetime) BETWEEN %s AND %s
            UNION
            SELECT 
                tct.ins_id,
                it.ins_name,
                COUNT(trial_cus_name) AS total_count, 
                COALESCE(SUM(CASE WHEN deal_yn = '是' THEN 1 ELSE 0 END), 0) AS dealed_qty,
                COALESCE(SUM(CASE WHEN deal_yn = '是' THEN 1 ELSE 0 END) / COUNT(trial_cus_name), 0) AS ratio
            FROM 
                trial_cls_table tct
            LEFT JOIN (
                SELECT ins_id, ins_name FROM ins_table 
            ) AS it ON it.ins_id = tct.ins_id
            WHERE 
                DATE(trial_datetime) BETWEEN %s AND %s
            GROUP BY 
                tct.ins_id, it.ins_name;

            '''
            cursor.execute(sql,(s_date,e_date,s_date,e_date))
            res=cursor.fetchall()
            result['trial_dealed_stat']=res

        except Exception as e:
            print('获取体验课数据错误：',e)
            cursor.close()
            conn.close()

        
        

        try:
            #正式课清单
            sql='''
                SELECT ins.ins_name,cls_type,cls_name,cus_name,cls_datetime as train_datetime from cls_tkn_rec_table t
                left JOIN
                (SELECT ins_id,ins_name from ins_table) as ins
                ON ins.ins_id=t.ins_id
                where date(cls_datetime) BETWEEN %s and %s 
                order by ins.ins_name desc,train_datetime asc;;
            '''
            cursor.execute(sql,(s_date,e_date))
            res=cursor.fetchall()
            result['formal_class_list']=res

            
            #常规课程计数
            sql='''
                SELECT t.ins_id,ins.ins_name,sum(cls_long) as trial_qty from cls_tkn_rec_table t
                left JOIN
                (select ins_id,ins_name from ins_table) as ins
                on ins.ins_id=t.ins_id
                where date(t.cls_datetime) BETWEEN %s and %s and t.cls_type='常规课程'
                group by t.ins_id,ins.ins_name
            '''

            cursor.execute(sql,(s_date,e_date))
            res=cursor.fetchall()
            result['formal_stat_cg']=res

            #限时课程计数
            sql='''
                SELECT t.ins_id,ins.ins_name,sum(cls_long) as trial_qty from cls_tkn_rec_table t
                left JOIN
                (select ins_id,ins_name from ins_table) as ins
                on ins.ins_id=t.ins_id
                where date(t.cls_datetime) BETWEEN %s and %s and t.cls_type='限时课程'
                group by t.ins_id,ins.ins_name
            '''

            cursor.execute(sql,(s_date,e_date))
            res=cursor.fetchall()
            result['formal_stat_lmt']=res

        except Exception as e:
            print('获取体验课数据错误：',e)
            cursor.close()
            conn.close()

        try:
            sql='''
            SELECT 
                cashier_table.cashier_id,
                cashier_table.cashier_name,
                COALESCE(cg.cg_realpay, 0) AS cg_realpay,
                COALESCE(lmt.lmt_realpay, 0) AS lmt_realpay,
                COALESCE(cg.cg_realpay, 0) + COALESCE(lmt.lmt_realpay, 0) AS total_realpay
            FROM 
                cashier_table
            LEFT JOIN 
                (SELECT 
                    cashier_id, 
                    SUM(real_pay) AS cg_realpay 
                FROM 
                    buy_rec_table 
                WHERE 
                    buy_type='常规课程' 
                    AND DATE(buy_date) BETWEEN %s AND %s 
                GROUP BY 
                    cashier_id) AS cg
            ON 
                cashier_table.cashier_id = cg.cashier_id
            LEFT JOIN 
                (SELECT 
                    cashier_id, 
                    SUM(real_pay) AS lmt_realpay 
                FROM 
                    buy_rec_table 
                WHERE 
                    buy_type='限时课程' 
                    AND DATE(buy_date) BETWEEN %s AND %s 
                GROUP BY 
                    cashier_id) AS lmt
            ON 
                cashier_table.cashier_id = lmt.cashier_id
             WHERE cashier_table.account_status='on';

            '''
            cursor.execute(sql,(s_date,e_date,s_date,e_date))
            res=cursor.fetchall()
            result['sale_stat']=res        


        except Exception as e:
            print('获取销售数据错误：',e)
            cursor.close()
            conn.close()


        try:
            sql='''
            SELECT 
                cashier_table.cashier_id,
                cashier_table.cashier_name,
                COALESCE(cg.cg_count, 0) AS cg_count,
                COALESCE(lmt.lmt_count, 0) AS lmt_count,
                COALESCE(cg.cg_count, 0) + COALESCE(lmt.lmt_count, 0) AS total_count
            FROM 
                cashier_table
            LEFT JOIN 
                (SELECT 
                    cashier_id, 
                    count(buy_date) AS cg_count
                FROM 
                    buy_rec_table 
                WHERE 
                    buy_type='常规课程' 
                    AND DATE(buy_date) BETWEEN %s AND %s 
                GROUP BY 
                    cashier_id) AS cg
            ON 
                cashier_table.cashier_id = cg.cashier_id
            LEFT JOIN 
                (SELECT 
                    cashier_id, 
                    count(buy_date) AS lmt_count
                FROM 
                    buy_rec_table 
                WHERE 
                    buy_type='限时课程' 
                    AND DATE(buy_date) BETWEEN %s AND %s 
                GROUP BY 
                    cashier_id) AS lmt
            ON 
                cashier_table.cashier_id = lmt.cashier_id
            WHERE cashier_table.account_status='on';

            '''
            cursor.execute(sql,(s_date,e_date,s_date,e_date))
            res=cursor.fetchall()
            result['sale_count_stat']=res        


        except Exception as e:
            print('获取销售数据错误：',e)
            cursor.close()
            conn.close()
        
        try:
            sql='''
            SELECT brt.buy_date,brt.cus_name,CONCAT(brt.buy_type, '/', brt.buy_cls_name) AS buy_type,CONCAT(brt.buy_num,'节/',brt.buy_cls_days,'天') as buy_num_days,brt.real_pay,ct.cashier_name FROM buy_rec_table brt
            LEFT JOIN cashier_table ct ON ct.cashier_id=brt.cashier_id
            WHERE brt.buy_date BETWEEN %s AND %s
            ORDER BY brt.buy_date;

            '''
            cursor.execute(sql,(s_date,e_date))
            res=cursor.fetchall()
            result['sale_list']=res        
        except Exception as e:
            print('获取销售数据错误：',e)
            cursor.close()
            conn.close()
            
        cursor.close()
        conn.close()

        return jsonify(result)

    @login_required
    def show_business(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./show_business.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)
    
    def get_train_summary(self):
        print('get train summary ... ')
        data=request.json
        cus_id=data['cus_id_name'][:7]
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            sql='''
                SELECT train_date, 
                CONCAT_WS(';', 
                            IFNULL(GROUP_CONCAT(DISTINCT muscle SEPARATOR ';'), NULL), 
                            IFNULL(GROUP_CONCAT(DISTINCT SUBSTRING_INDEX(oxy_name, '-', -1) SEPARATOR ';'), NULL)
                            ) AS merged_content,
                COALESCE(MAX(comment),'-') as train_comment
                FROM (
                    SELECT date(t.train_datetime) as train_date, t.muscle, NULL AS oxy_name,comment
                    FROM train_nonoxy_rec_table t
                    WHERE t.cus_id=%s
                    
                    UNION ALL
                    
                    SELECT date(k.train_datetime) as train_date, NULL AS muscle, k.oxy_name,comment
                    FROM train_oxy_rec_table k
                    WHERE k.cus_id=%s
                ) AS combined_tables
                GROUP BY train_date
                ORDER BY train_date DESC 
                LIMIT 5
            '''
            cursor.execute(sql,(cus_id,cus_id))
            train_sum=cursor.fetchall()
            # print('train sum:',train_sum)
            return jsonify({'res':'ok','train_sum':train_sum})

        except Exception as e:
            print('get train summer error: ',e)
            return jsonify({'res':'failed','data':None})
        finally:
            cursor.close()
            conn.close()
        

    def get_industry_occu(self):
        conn=self.connect_mysql()
        
        try:
            sql='''
                select * from industry_table
            '''
            cursor=conn.cursor()
            cursor.execute(sql)
            industries=cursor.fetchall()
        except:
            pass
        

        try:
            sql='''
                select * from occu_table
            '''
            cursor=conn.cursor()
            cursor.execute(sql)
            occus=cursor.fetchall()
        except:
            pass
    
        cursor.close()
        conn.close()

        return jsonify({'industries':industries,'occus':occus})


    def show_who_ins(self):
        print('sending show who ins...')
        config_show_who_fn=os.path.join(os.path.dirname(os.path.realpath(__file__)),'config','show_who.config')
        with open(config_show_who_fn,'r',encoding='utf-8') as f:
            config_show_who=json.load(f)
        return jsonify(config_show_who)
    
    def reedit_pwd_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return  render_template('./reedit_pwd.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    def deal_reedit_pwd_db(self):
        print('deal reedit pwd')
        data=request.json

        user_id=data['user_id']
        edited_pwd=data['edited_pwd']
        salt=str(random.randint(10000000,99999999))

        pwd_salt=hashlib.sha256((edited_pwd + salt).encode()).hexdigest()
        conn=self.connect_mysql()
        cursor=conn.cursor()
        try: 
            #如果没有记录，写入salt值，如有，更新。
            sql=f'select  ins_id from salt_table where ins_id=%s'
            cursor.execute(sql,(user_id))
            res=cursor.fetchone()
            if res:
                sql=f'update salt_table set salt=%s where ins_id=%s'
                cursor.execute(sql,(salt,user_id))
            else:
                sql=f'insert into salt_table (ins_id,salt) values (%s,%s)'
                cursor.execute(sql,(user_id,salt))                

            sql=f'update ins_table set pwd=%s, first_login=0 where ins_id=%s'
            cursor.execute(sql,(pwd_salt,user_id))

            conn.commit()
            cursor.close()
            conn.close()
            return jsonify({'res':'ok'})            
        except Exception as e:
            print(e)
            cursor.close()
            conn.close()
            return jsonify({'res':'failed'})
            
    def get_wxprg_ads_pic(self):    
        with open (os.path.join(os.path.dirname(__file__),'config','wxprg_ads.config'),'r',encoding='utf-8') as f:
            ads_url=json.load(f)
        
        return {'ads_urls':ads_url}

    def send_ads_pic(self):
        return render_template('./ads.html')

    def login(self):
        if session.get('logged_in'):
            return redirect(url_for('main'))
        return render_template('./login.html')
    
    def login_check(self):        
        data=request.json        

        if data:
            try:
                mobile,pwd=data['user'],data['pwd']
                conn=self.connect_mysql()
                cursor=conn.cursor()
                sql='select ins_id,ins_name from ins_table where mobile=%s'

                cursor.execute(sql,(mobile))
                res=cursor.fetchone()
                # print(res)
                if res:
                    ins_id=str(res['ins_id'])
                else:
                    raise ValueError('没有手机号对应的ID')

                #读取salt
                sql='select ins_id,salt from salt_table where ins_id=%s'
                
                cursor.execute(sql,(ins_id))
                res=cursor.fetchone()
                # print('226',res)
                if res:
                    salt=str(res['salt'])
                    pwd=hashlib.sha256((pwd + salt).encode()).hexdigest()
                    # hashed_password = hashlib.sha256((password + salt).encode()).hexdigest()

                sql='select ins_name,role,first_login from ins_table where ins_id=%s and pwd=%s'            
                cursor.execute(sql,(ins_id,pwd))
                res=cursor.fetchone()
                # print(res)
                session['logged_in'] = True
                session['user']=res['ins_name']
                session['role']=res['role']
                session['ins_id']=ins_id

                # print(res['ins_name'],res['role'])
                if int(res['first_login'])==1:
                    return render_template('/reedit_pwd_page')
                else:
                    return render_template('/index')
            except Exception as e:
                print(e)
                return jsonify({'res':'failed'})

        else:
            return render_template('/reedit_pwd_page')

        

    def deal_login(self):
        data=request.json
        mobile,pwd=data['user'],data['pwd']
        conn=self.connect_mysql()



        with conn.cursor(cursor=pymysql.cursors.DictCursor) as cursor:
            print('matching login information...')
            try:
                #通过手机号码读取ID
                sql='select ins_id,ins_name from ins_table where mobile=%s'
                cursor.execute(sql,(mobile))
                res=cursor.fetchone()
                # print(res)
                if res:
                    ins_id=str(res['ins_id'])
                else:
                    raise ValueError('没有手机号对应的ID')

                #读取salt
                sql='select ins_id,salt from salt_table where ins_id=%s'
                
                cursor.execute(sql,(ins_id))
                res=cursor.fetchone()
                # print('226',res)
                if res:
                    salt=str(res['salt'])
                    pwd=hashlib.sha256((pwd + salt).encode()).hexdigest()
                    # hashed_password = hashlib.sha256((password + salt).encode()).hexdigest()

                sql='select ins_name,role,first_login from ins_table where ins_id=%s and pwd=%s'            
                cursor.execute(sql,(ins_id,pwd))
                res=cursor.fetchone()
                # print(res)
                session['logged_in']=True
                session['user']=res['ins_name']
                session['role']=res['role']
                session['ins_id']=ins_id

                # print(res['ins_name'],res['role'])
                if int(res['first_login'])==1:
                    return jsonify({'res':'ok','url':url_for('reedit_pwd_page'),'ins_id':ins_id,'reedit_pwd':'yes'})
                else:
                    return jsonify({'res':'ok','url':url_for('index'),'ins_id':ins_id,'reedit_pwd':'no'})
            except Exception as e:
                print(e)
                return jsonify({'res':'failed'})

    def get_cards(self):
        print('get valid cards')
        data=request.json
        cus_id=data['cus_id_name'][:7]

        conn=self.connect_mysql()
        cursor=conn.cursor()
        #获取该会员上课次数
        sql='select sum(cls_long) as tkn_qty from cls_tkn_rec_table where cus_id=%s'
        cursor.execute(sql,cus_id)
        qty=cursor.fetchone()[0]
        
        today=datetime.datetime.now().strftime('%Y-%m-%d')


        sql='''
        select card_id,card_type,card_name,cls_qty,card_start_time,prd,end_time,cmt from
        cards_table 
        where card_id in (select card_id from cardholder_card_table where cus_id=%s) and cls_qty>=%s and end_time>=%s;

        '''
        try:
            cursor.execute(sql,(cus_id,qty,today))
            res=cursor.fetchall()
            print(res)

            return {'res':'ok','valid_cards':res}
        except:
            print('get cards id failed')
            return {'res':'failed'}



    def logout(self):
        session.pop('user',None)
        session.pop('role',None)
        session.pop('ins_id',None)
        session.pop('logged_in',None)
        return redirect(url_for('login'))

    def write_ins_book_db(self):
        try:
            data=request.json
            conn=self.connect_mysql()
            cursor=conn.cursor()

            # print(data)

            #通过教练ins_id获取ins_name
            
            sql=f'''
                SELECT ins_id,ins_name FROM ins_table WHERE ins_id=%s ;
            '''
            cursor.execute(sql,data['insId'])
            data['insName']=cursor.fetchone()[1]
            
            col_names=['date','insId','insName','0600','0630','0700','0730','0800','0830','0900','0930','1000','1030','1100','1130','1200','1230','1300','1330','1400','1430','1500','1530','1600','1630','1700','1730','1800','1830','1900','1930','2000','2030','2100','2130','comment']
            sorted_data={key:data[key] for key in col_names}
            values=list(sorted_data.values())
            # print(values)
            #查询是否已有数据
            sql=f'''
                SELECT ins_id,ins_name FROM ins_book_table WHERE ins_id=%s and date=%s;
            '''
            cursor.execute(sql,[data['insId'],data['date']])
            res=cursor.fetchall()
            #如已有教练、日期 数据，更新
            if res:
                #update的时候，加上这些参数
                values.extend([data['insId'],data['insName'],data['date']])
                # print(values)
                print('old data,updating')     
                sql=f'''
                        update ins_book_table
                        set 
                        date=%s,ins_id=%s,ins_name=%s,`0600`=%s,`0630`=%s,`0700`=%s,`0730`=%s,`0800`=%s,`0830`=%s,`0900`=%s,`0930`=%s,`1000`=%s,`1030`=%s,`1100`=%s,`1130`=%s,`1200`=%s,`1230`=%s,`1300`=%s,`1330`=%s,`1400`=%s,`1430`=%s,`1500`=%s,`1530`=%s,`1600`=%s,`1630`=%s,`1700`=%s,`1730`=%s,`1800`=%s,`1830`=%s,`1900`=%s,`1930`=%s,`2000`=%s,`2030`=%s,`2100`=%s,`2130`=%s,comment=%s
                        where
                        ins_id=%s and ins_name=%s and date=%s
                    '''
                cursor.execute(sql,values)
                conn.commit()
                cursor.close()
                conn.close()     
                return  jsonify({'res':'old data:update in booking table successfully.'})  
            #如无教练、日期 数据，插入
            else:           
                print('new data,inserting')     
                
                sql=f'''
                        insert into ins_book_table
                        (date,ins_id,ins_name,`0600`,`0630`,`0700`,`0730`,`0800`,`0830`,`0900`,`0930`,`1000`,`1030`,`1100`,`1130`,`1200`,`1230`,`1300`,`1330`,`1400`,`1430`,`1500`,`1530`,`1600`,`1630`,`1700`,`1730`,`1800`,`1830`,`1900`,`1930`,`2000`,`2030`,`2100`,`2130`,comment)
                        values
                        (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
                    '''
                cursor.execute(sql,values)
                conn.commit()
                cursor.close()
                conn.close()     
                return  jsonify({'res':'new data:write in booking table successfully.'})      
        except Exception as e:
            print('write into ins book table error: ',e)
            return  jsonify({'res':'error'+e})
        
       

    def get_book_data_db(self):
        print('from llz database,get ins book data')
        data=request.json
        # print(data)
        conn=self.connect_mysql()
        cursor=conn.cursor()
        sql=f'''
            SELECT ins_name FROM ins_table WHERE ins_id=%s ;
        '''
        cursor.execute(sql,data['ins_id'])
        res_ins_name=cursor.fetchone()[0]

        sql=f'''
            SELECT * FROM ins_book_table WHERE ins_id=%s and ins_name=%s and date=%s;
        '''

        cursor.execute(sql,[data['ins_id'],res_ins_name,data['date']])
        # print([data['ins_name'],res_ins_id,data['date']])
        ins_book_data=cursor.fetchall()
        # print(ins_book_data)
        if ins_book_data:
            cols=['id','date','ins_id','ins_name','0600','0630','0700','0730','0800','0830','0900','0930','1000','1030','1100','1130','1200','1230','1300','1330','1400','1430','1500','1530','1600','1630','1700','1730','1800','1830','1900','1930','2000','2030','2100','2130','comment']
            ins_book_dic={}
            for n,col in enumerate(cols):
                ins_book_dic[col]=ins_book_data[0][n]
        else:
            ins_book_dic=''

        cursor.close()
        conn.close()
        # print(ins_book_dic)
        return jsonify({'ins_book_data':ins_book_dic})

    def ins_book_page(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./ins_book.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    def test_copy(self):
        return render_template('./test_copy.html')
  

    def write_trial_rec(self):
        try:
            data=request.json
            fn=os.path.join(self.config_lz['work_dir'],'03-教练管理','体验课上课记录表.xlsx')
            df_old=pd.read_excel(fn,sheet_name='体验课上课记录表')
            df_old.dropna(subset=['体验客户姓名'],inplace=True)

            app=xw.App(visible=False)
            wb=app.books.open(fn)
            sht=wb.sheets['体验课上课记录表']
            row = df_old.shape[0]+2
            sht.range(f'B{row}').value=data['dateString']
            sht.range(f'C{row}').value=data['timeString']
            sht.range(f'D{row}').value=1
            sht.range(f'E{row}').value=data['cusNameInput']
            sht.range(f'F{row}').value=data['mobilePhone']
            sht.range(f'G{row}').value=data['ins']
            sht.range(f'H{row}').value='是'
            sht.range(f'I{row}').value=data['cusSource']
            sht.range(f'J{row}').value=data['comment']

            wb.save(fn)
            wb.close()
            app.quit()
        except Exception as e:
            print('写入体验课表错误：',e)
            return '写入体验课表成功'+e

        return '写入体验课表成功'
        
    def write_trial_rec_db(self):
        try:
            data=request.json
            conn=self.connect_mysql()
            cursor=conn.cursor()
            # sql=f'select ins_id,ins_name from ins_table where ins_id=%s'
            # cursor.execute(sql,(data['insId']))
            # ins_name_res=cursor.fetchone()
            # data['insName']=ins_name_res[1]
            data['trial_cls_long']=1
            data['datetime']=data['dateString']+' '+data['timeString']
            data['finish_yn']='是'
            data['deal_yn']='否'
            data['deal_date']=None
            data['formal_cus_id_name']=None
            del data['dateString']
            del data['timeString']
            data_cols=['datetime','trial_cls_long','cusNameInput','cusBirthday','mobilePhone','insId','finish_yn','cusSource','trialProblem','comment','deal_yn','deal_date','formal_cus_id_name']
            sorted_data={key:data[key] for key in data_cols}
            values=tuple(sorted_data.values())
            # values = ', '.join(f'"{data[key]}"' if sorted_data[key] is not None else 'NULL' for key in data_cols)
            # print(values)            
            
            sql='''
                    insert into trial_cls_table (trial_datetime,trial_cls_long,trial_cus_name,trial_cus_birthday,trial_cus_mobile,ins_id,finish_yn,trial_cus_source,trial_problem,comment,deal_yn,deal_date,formal_cus_id_name)
                    values 
                    (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
                '''
            cursor.execute(sql,values)
            conn.commit()
            cursor.close()
            conn.close()

        except Exception as e:
            print('写入体验课表错误：',e)
            return '写入体验课表错误'+e

        return '写入体验课表成功'

    @login_required
    def trial_class(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./trial_class.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)
         
    def date_to_string(self,obj,format='date'):
        try:
            if format=='date':
                return obj.strftime('%Y-%m-%d')
            elif format=='time':
                return obj.strftime('%H:%M')+':00'
        except:
            return None

        raise TypeError(f"Object of type {obj.__class__.__name__} is not JSON serializable")

    def get_trial_list(self):
        
        df_trial=pd.read_excel(os.path.join(self.config_lz['work_dir'],'03-教练管理','体验课上课记录表.xlsx'),sheet_name='体验课上课记录表')
        df_trial.dropna(subset=['体验客户姓名'],inplace=True)
        df_trial.fillna('',inplace=True)
        df_trial['体验课日期']=df_trial['体验课日期'].apply(lambda x:self.date_to_string(x,'date'))
        df_trial['体验课时间']=df_trial['体验课时间'].apply(lambda x:self.date_to_string(x,'time'))
        df_trial['出单日期']=df_trial['出单日期'].apply(lambda x:self.date_to_string(x,'date'))
        print(df_trial.dtypes)
        dic_trial=df_trial.to_dict()
        num_keys=len(dic_trial[list(dic_trial.keys())[0]])
        result={}
        for i in range(num_keys):
            result[i] = {key: dic_trial[key][i] for key in dic_trial}
        # print(result)
        print(type(result))

        # dic_trial=df_trial.T.to_dict()

        return jsonify({'all_trial':result})

    def get_trial_list_db(self):
        conn=self.connect_mysql()
        cursor = conn.cursor()

        # 执行SQL查询
        # sql = "SELECT * FROM `id_table`"
        sql="select id,trial_datetime,trial_cls_long,trial_cus_name,trial_cus_birthday,trial_cus_mobile,ins_id,finish_yn,trial_cus_source,trial_problem,comment,deal_yn,deal_date,formal_cus_id_name from trial_cls_table"
        cursor.execute(sql)

        # # 获取查询结果
        results = cursor.fetchall()

        # data=((1, datetime.datetime(2022, 5, 14, 9, 0), 1, 'TS0501唐家源', None, '韦越棋', '是', '小红书', None, '是', datetime.date(2022, 5, 22), None), (2, datetime.datetime(2022, 5, 23, 19, 30), 1, 'TS0502毛圆', None, '韦越棋', '是', '小红书', None, None, None, None))
        if results:
            df=pd.DataFrame(results)
            df.columns=['序号','体验课日期时间','时长','体验客户姓名','体验客户生日','体验客户手机','教练','是否完成','客户来源','体测发现','备注','是否出单','出单日期','出单后正式会员编码及姓名']
            df['体验课日期']=df['体验课日期时间'].dt.strftime('%Y-%m-%d')
            df['体验课时间']=df['体验课日期时间'].dt.strftime('%H:%M:%S')
            df=df[['序号','体验课日期','体验课时间','时长','体验客户姓名','体验客户生日','体验客户手机','教练','是否完成','客户来源','体测发现','备注','是否出单','出单日期','出单后正式会员编码及姓名']]

            dic_trial_list=df.T.to_dict()
        else:
            df=pd.DataFrame(columns=['序号','体验课日期','体验课时间','时长','体验客户姓名','体验客户生日','体验客户手机','教练','是否完成','客户来源','体测发现','备注','是否出单','出单日期','出单后正式会员编码及姓名'])
            dic_trial_list=df.T.to_dict()

        # 关闭游标和连接
        cursor.close()
        conn.close()

        return jsonify({'all_trial':dic_trial_list})

    def deal_start_class_page(self):
        data=request.json
        
        try:
            self.add_rec_to_start_class_table(dic=data)
            self.delete_rec_in_aux_table(dic=data)
            return jsonify({'result':'写入限时课程表及辅助表成功'})
        except Exception as e:
            print('写入限时课程表或辅助表错误：',e)
            return jsonify({'result':'写入限时课程表及辅助表成功错误'+e})

    def deal_start_class_page_db(self):    
        try:
            data=request.json
            print(data)
            conn=self.connect_mysql()
            cursor=conn.cursor()

            data['cus_id']=data['cus_name_input'][:7].strip()
            data['cus_name']=data['cus_name_input'][7:].strip()
            del data['cus_name_input']
            data_cols=['cus_id','cus_name','card_id','start_date','end_date','ins_id','opr_id','opr_type','prd','valid_time','cls_qty','card_type','cls_name']
            sorted_data={key: data[key] for key in data_cols}
            # print(sorted_data)
            values=tuple(sorted_data.values())
            
            # sql='''
            #     insert into lmt_cls_rec_table
            #     (cus_id,cus_name,buy_code,start_date,end_date,ins_id,operator_id)
            #     values
            #     (%s,%s,%s,%s,%s,%s,%s)
            # '''
            current_time=datetime.datetime.now()
            if sorted_data['opr_type']=='新课':
                #修改原记录
                # sql='''
                # update cards_table 
                # set card_start_time=%s,end_time=%s,opr_time=%s
                # where card_id=%s
                # '''             

                # cursor.execute(sql,(sorted_data['start_date'],sorted_data['end_date'],current_time,sorted_data['card_id']))
                # conn.commit()


                #插入记录

                #找到原来的备注
                sql='''
                    select cmt from cards_table where card_id=%s order by id limit 1;
                '''
                cursor.execute(sql,sorted_data['card_id'])
                res=cursor.fetchone()
                cmt=res[0]


                sql='''
                insert into cards_table 
                (card_id,card_type,card_name,cls_qty,card_start_time,prd,valid_time,end_time,cmt,opr_id,opr_time)
                values
                (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
    
                '''
                values=(sorted_data['card_id'],sorted_data['card_type'],sorted_data['cls_name'],sorted_data['cls_qty'],
                        sorted_data['start_date'],sorted_data['prd'],sorted_data['valid_time'],sorted_data['end_date'],cmt,
                        sorted_data['opr_id'],current_time)
                cursor.execute(sql,(values))
                conn.commit()


            else: #延长
                #找到原来的备注
                sql='''
                    select cmt from cards_table where card_id=%s order by id limit 1;
                '''
                cursor.execute(sql,sorted_data['card_id'])
                res=cursor.fetchone()
                cmt=res[0]

                sql='''
                insert into cards_table 
                (card_id,card_type,card_name,cls_qty,card_start_time,prd,valid_time,end_time,cmt,opr_id,opr_time)
                values
                (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
    
                '''
                values=(sorted_data['card_id'],sorted_data['card_type'],sorted_data['cls_name'],sorted_data['cls_qty'],
                        sorted_data['start_date'],sorted_data['prd'],sorted_data['valid_time'],sorted_data['end_date'],cmt,
                        sorted_data['opr_id'],current_time)
                cursor.execute(sql,(values))
                conn.commit()


            # #从未开课表中删除对应的购课记录，如无，mysql也不会报错
            # sql=f'''
            #     delete from  not_start_lmt_table
            #     where
            #     buy_code=%s
            # '''
            # cursor.execute(sql,(sorted_data['buyCode']))

            # conn.commit()
            # cursor.close()
            # conn.close()



            cursor.close()
            conn.close()
            return jsonify({'res':'ok'})
        except Exception as e:
            print('写入限时课程表或辅助表错误：',e)
            return jsonify({'res':'failed','info':'写入限时课程表及删除未开课表错误'+e})
        

   
    def add_rec_to_start_class_table(self,dic):
        fn=os.path.join(self.config_lz['work_dir'],'01-会员管理','会员资料',dic['cusName'].strip()+'.xlsm')

        df=pd.DataFrame(dic,index=[0])
        df=df[['buyCode','startDate','endDate']]
        df_old=pd.read_excel(fn,sheet_name='限时课程记录')

        app=xw.App(visible=False)
        wb=app.books.open(fn)
        sht=wb.sheets['限时课程记录']
        row = df_old.shape[0]+2
        rng='A'+str(row)+':C'+str(row)
        sht.range(rng).value=df.iloc[0].tolist()

        wb.save(fn)
        wb.close()
        app.quit()

    
    def add_rec_in_aux_table(self,buy_code):
        fn=os.path.join(self.config_lz['work_dir'],'01-会员管理','会员资料',buy_code[:-8].strip()+'.xlsm')
        df_old=pd.read_excel(fn,sheet_name='辅助表')
        df_old=df_old[['未开课的购课编码']]
        df_old.dropna(how='any',inplace=True)

        app=xw.App(visible=False)
        wb=app.books.open(fn)
        sht=wb.sheets['辅助表']
        row = df_old.shape[0]+2
        sht.range('I'+str(row)).value=buy_code

        wb.save(fn)
        wb.close()
        app.quit()
        print('写入辅助表成功')
        return '限时课程写入辅助表成功'

    def delete_rec_in_aux_table(self,dic):
        fn=os.path.join(self.config_lz['work_dir'],'01-会员管理','会员资料',dic['cusName'].strip()+'.xlsm')
        app=xw.App(visible=False)
        wb=app.books.open(fn)
        sht=wb.sheets['辅助表']
        column = sht.range('I2:I20')
        value_to_delete=dic['buyCode']
    
        # 在列中查找指定值并删除它
        for cell in column:
            if cell.value == value_to_delete:
                cell.delete('up')

        wb.save(fn)
        wb.close()
        app.quit()

    @login_required 
    def main(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('main.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)
    
        
    def index(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('index.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def start_limit_class(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./start_limit_class.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    def write_body_db(self):
        try:
            data=request.json
            # print(data)
            conn=self.connect_mysql()
            cursor=conn.cursor()
            cus_id,cus_name=data['cusName'][:7],data['cusName'][7:]
            sql=f"select sex,birthday from cus_info_table where cus_id='{cus_id}' and cus_name='{cus_name}'"
            cursor.execute(sql)
            res=cursor.fetchall()
            sex=res[0][0]
            birthday=res[0][1].strftime('%Y%m%d')
            bfr=self.bfr(sex,birthday,float(data['ht']),float(data['wt']),float(data['waist']))
            # print(sex,birthday,bfr)
            data['bfr']=bfr
            data['cus_id']=cus_id
            data['cus_name']=cus_name
            del data['cusName']
            sorted_cols=['cus_id','cus_name','date','ht','wt','bfr','chest','l_arm','r_arm','waist','hip','l_leg','r_leg','l_calf','r_calf','heart','balance','power','flex','core','body_problems']
            sorted_data={key:data[key] for key in sorted_cols}
            for key,value in sorted_data.items():
                try:
                    sorted_data[key]=float(value)
                except:
                    pass
            
            #写入体测表
            value=tuple(sorted_data.values())
            sql=f"insert into body_msr_table (cus_id,cus_name,msr_date,ht,wt,bfr,chest,l_arm,r_arm,waist,hip,l_leg,r_leg,l_calf,r_calf,heart,balance,power,flex,core,body_problems) values {value}"
            # print(value,sql)
            cursor.execute(sql)

            #写入训练计划表
            value=[data['cus_id'],data['cusGoal'],data['insSuggest'],data['stage']['stageSelect'],data['stage']['stageGoal'],
                   data['trainSummary'],data['comp_pic_name'],data['insSelect'],data['date'],
                   data['opr_id'],data['opr_time']]
            sql='''
                insert into train_plan_table (cus_id,cus_goals,ins_suggests,stage,stage_goal,train_summary,comp_pic_url,ins_id,plan_date,opr_id,opr_time) 
                values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
            '''
            cursor.execute(sql,value)

            #写入阶段计划表
            

            
            # 指定的顺序
            order = ['purposeTxt', 'partTxt', 'frqcyTxt', 'strengthTxt', 'benifitTxt', 'typeTxt', 'prdTxt', 'activeTxt']

            # 获取最大索引号
            max_idx = max(int(key.split('-')[1]) for key in data['stage']['detailRecs'].keys())

            # 生成列表
            results = [[data['cus_id'],data['insSelect']]+[data['stage']['detailRecs'].get(f"{field}-{i}", "") for field in order] +[data['date'],data['opr_id'],data['opr_time']] for i in range(max_idx + 1)]
            
            # print(results)

            sql='''
                insert into train_stage_detail_table (cus_id,ins_id,train_purpose,train_part,train_frqcy,train_strength,train_benifit,train_type,train_prd,train_active,plan_date,opr_id,opr_time) 
                values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
            '''
            cursor.executemany(sql,results)


            conn.commit()
            cursor.close()
            conn.close()

            print('write into train plan table successfully.')
            
            return jsonify({'res':'ok'})

        except Exception as e:
            print('write body error in write_body_db():',e)
            return  jsonify({'res':'failed','error':str(e)})

    def bfr(self,sex,birthday,ht,wt,waist):
        bfr_test=get_data.cals()       
        
        if birthday:
           # 可能的日期格式列表
            date_formats = ['%Y', '%Y%m', '%Y%m%d']
            for date_format in date_formats:
                try:
                    birthday = datetime.datetime.strptime(str(birthday), date_format)
                    age = relativedelta(datetime.datetime.now(), birthday).years
                    bfr = bfr_test.bfr(age=age, sex=sex, ht=ht, wt=wt, waist=waist, adj_bfr='no', adj_src='prg', formula=1)
                    return bfr
                except ValueError as e:
                    # print('bfr计算错误 in bfr():',e)
                    continue
        else:
            bfr=0
        return bfr


    #将dic的数据整理为按0，1，2的形式以输出给前端
    def dic_format(self,dic,order_name,transfer_int_64='no'):
        formatted_data = {}
        # 遍历原始数据，并根据需要构建新的格式化数据

        if transfer_int_64=='yes':
            dic = dic.astype(object).where(pd.notnull(dic), None)

        for i in range(len(dic[order_name])):
            items={}
            for key,value in dic.items():
                items[key]=dic[key][i]                
            formatted_data[str(i)]=items
        return formatted_data

    def dic_format2(self,dic, order_name, transfer_int_64='no'):
        # 确保 DataFrame 中所有数据类型都适合 JSON 格式
        if transfer_int_64 == 'yes':
            dic = dic.astype(object).where(pd.notnull(dic), None)

        # 格式化数据
        formatted_data = {}
        for i, row in dic.iterrows():
            # 使用 iterrows 以便直接操作每一行
            items = {col: (row[col].isoformat() if isinstance(row[col], pd.Timestamp) else row[col]) 
                 for col in order_name}
            formatted_data[str(i)] = items
        return formatted_data

    
    def get_body_history(self):
        cus_name=request.data.decode('utf-8')
        print(f'\n{cus_name}')
        fn=os.path.join(self.config_lz['work_dir'],'01-会员管理','会员资料',cus_name+'.xlsm')
        df_body=pd.read_excel(fn,sheet_name='身体数据')
        df_basic=pd.read_excel(fn,sheet_name='基本情况')

        sex=df_basic['性别'].tolist()[0]
        birthday=df_basic['出生年月'].tolist()[0]

        # print(df_body)
        if df_body.empty:
            empty_body={'0': {'日期': '', '身高（cm）': '', '体重（Kg）': '', '体脂率': '', 
                        '胸围': '', '左臂围': '', '右臂围': '', '腰围': '', '臀围': '', '左腿围': '', 
                        '右腿围': '', '左小腿围': '', '右小腿围': '', '心肺': '', '平衡': '', 
                        '力量': '', '柔韧性': '', '核心': ''}}
            return jsonify(empty_body)
        else:
            df_body.dropna(subset=['日期'],inplace=True)
            df_body.fillna(0,inplace=True)
            
            dic_body=df_body.to_dict()

            formatted_data=self.dic_format(dic=dic_body,order_name='日期')

        # 计算bfr
        try:
            for key,item in formatted_data.items():
                item['体脂率']=self.bfr(sex,birthday,item['身高（cm）'],item['体重（Kg）'],item['腰围'])
        except Exception as e:
            print('bfr计算错误 in get_body_history()',e)
            item['体脂率']='-'

        # print(formatted_data)
        # print(dic_body)
        return jsonify(formatted_data)
    
    def get_body_history_db(self):
        # cus_id_name='MH00008苏云'
        cus_id_name=request.data.decode('utf-8')
        cus_id,cus_name=cus_id_name[:7],cus_id_name[7:]

        conn=self.connect_mysql()
        cursor=conn.cursor()
        sql=f"select * from body_msr_table WHERE cus_name='{cus_name}' and cus_id='{cus_id}'"
        cursor.execute(sql)
        body_history=cursor.fetchall()
        body_history_str=[]
        if body_history:
            for r,row in enumerate(body_history):
                row_body_history=[]
                for c,value in enumerate(row):
                    if isinstance(value,datetime.date):
                        row_body_history.append(value.strftime('%Y-%m-%d'))
                    elif isinstance(value,Decimal):
                        row_body_history.append(float(value))
                    else:
                        row_body_history.append(value)
                body_history_str.append(row_body_history)        
            body_history_cols=['id','cus-id','cus_name','日期','身高（cm）','体重（Kg）','体脂率','胸围','左臂围','右臂围','腰围','臀围','左腿围','右腿围','左小腿围','右小腿围','心肺','平衡','力量','柔韧性','核心','体测问题']
            dic_body_history=self.mysql_list_data_to_dic(data=body_history_str,mysql_cols=body_history_cols)
        else:
            dic_body_history={'0': {'id':'','cus-id':'','cus_name':'','日期': '', '身高（cm）': '', '体重（Kg）': '', '体脂率': '', 
                        '胸围': '', '左臂围': '', '右臂围': '', '腰围': '', '臀围': '', '左腿围': '', 
                        '右腿围': '', '左小腿围': '', '右小腿围': '', '心肺': '', '平衡': '', 
                        '力量': '', '柔韧性': '', '核心': '','体测问题':''}}
        

        cursor.close()
        conn.close()

        return jsonify(dic_body_history)
    
    @login_required
    def input_body(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('./input_body.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    def deal_cls(self):
        cls_data=request.json

        cls_rec=cls_data['cls_tkn']
        # train_rec=cls_data['train_rec']
        # 写入上课记录
        cls_tkn_res=self.write_cls_tkn(cls_rec)

        # 写入训练情况
        train_rec_res=self.write_train_rec(cls_data)

        # print(cls_data)
        return cls_tkn_res+'\n'+train_rec_res

    def deal_cls_db(self):
        try:
            data=request.json
            conn=self.connect_mysql()
            cursor=conn.cursor()

            #教练上课记录
            cls_tkn_rec=data['cls_tkn']
            cls_tkn_rec['cus_id']=cls_tkn_rec['cus_name'][:7]
            cls_tkn_rec['cus_name']=cls_tkn_rec['cus_name'][7:]
            cls_tkn_rec['cls_datetime']=cls_tkn_rec['cls_tkn_date']+' '+cls_tkn_rec['cls_tkn_time']
            cus_id=cls_tkn_rec['cus_id']
            cus_name=cls_tkn_rec['cus_name']
            card_id=cls_tkn_rec['card_id']
            ins_id=cls_tkn_rec['ins_id']
            ins_name=cls_tkn_rec['ins_name']
            basic_cls_comment=cls_tkn_rec['basic_cls_comment']
            train_datetime=cls_tkn_rec['cls_datetime']

            del cls_tkn_rec['cls_tkn_date']
            del cls_tkn_rec['cls_tkn_time']

            cls_tkn_data_cols=['cus_id','cus_name','cls_datetime','cls_long','cls_type','cls_name','card_id','ins_id','ins_name','basic_cls_comment','operator_id','operate_time']
            sorted_cls_tkn_data={key: cls_tkn_rec[key] for key in cls_tkn_data_cols} 
            values_cls_tkn=tuple(sorted_cls_tkn_data.values())
            
            
            sql_cls_tkn='''
                insert into cls_tkn_rec_table
                (cus_id,cus_name,cls_datetime,cls_long,cls_type,cls_name,card_id,ins_id,ins_name,comment,operator_id,operate_time) 
                values 
                (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
            '''
            cursor.execute(sql_cls_tkn,values_cls_tkn)

            #训练记录
            train_data=data['train_rec']
            #替换''为'0'
           
            

            calories=train_data['calories']
            train_comment=train_data['trainComment']
            train_items=train_data['train_recs']

            
           
            oxy_items=[]
            non_oxy_items=[]
            for train_item in train_items:
                # 抗阻运动
                non_oxy_row=[]
                oxy_row=[]
                if train_item['nonOxyName']:   
                    action_name=train_item['nonOxyName']
                    sql=f'select muscle from train_item_table where action_name="{action_name}"'
                    cursor.execute(sql)
                    muscle=cursor.fetchall()[0][0]
                    non_oxy_row.extend([train_datetime,cus_id,cus_name,muscle,action_name,train_item['nonOxyWt'],
                                            train_item['nonOxyDis'],train_item['nonOxyNum'],train_item['nonOxyGroup'],
                                            calories,card_id,ins_id,ins_name,train_comment])
                if non_oxy_row:
                    non_oxy_row[-1]=non_oxy_row[-1] if non_oxy_row[-1] else None
                    non_oxy_items.append(non_oxy_row)

                if train_item['oxyName']:                   
                    oxy_row.extend([train_datetime,cus_id,cus_name,train_item['oxyName'],train_item['oxyTime'],
                                    train_item['oxyGroup'],calories,card_id,ins_id,ins_name,train_comment])
                if oxy_row:
                    oxy_row[-1]=oxy_row[-1] if oxy_row[-1] else None
                    oxy_items.append(oxy_row)

            #将''替换为'0',再将数字转换为浮点数      
            oxy_items = [[item if item != '' else '0' for item in sublist] for sublist in oxy_items]     
            non_oxy_items = [[item if item != '' else '0' for item in sublist] for sublist in non_oxy_items]    
            # #评论为空                  
            # oxy_items[-1]=''        
            # non_oxy_items[-1]=''
            try:
                oxy_items = [[float(item) if item.replace('.', '', 1).isdigit() else item for item in sublist] for sublist in oxy_items]
                non_oxy_items = [[float(item) if item.replace('.', '', 1).isdigit() else item for item in sublist] for sublist in non_oxy_items]
            except:
                pass
            
            # print(oxy_items,non_oxy_items)

            #无氧训练写入
            non_oxy_train_sql=f'''
                insert into train_nonoxy_rec_table 
                (train_datetime,cus_id,cus_name,muscle,non_oxy_name,non_oxy_wt,non_oxy_dis,non_oxy_num,non_oxy_group,calories,card_id,ins_id,ins_name,comment) 
                values 
                (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s);
            '''
            cursor.executemany(non_oxy_train_sql,non_oxy_items)
            
            #有氧训练写入
            oxy_train_sql=f'''
                insert into train_oxy_rec_table 
                (train_datetime,cus_id,cus_name,oxy_name,oxy_time,oxy_group,calories,card_id,ins_id,ins_name,comment) 
                values 
                (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s);

            '''
            cursor.executemany(oxy_train_sql,oxy_items)

            conn.commit()
            cursor.close()
            conn.close()


            # return '写入上课记录及训练记录成功'
            return jsonify({'res':'ok','data':'写入上课记录及训练记录成功'})
        except Exception as e:
            print('deal_cls_db() ERROR',e)
            # return  '写入上课记录及训练记录错误'
            return jsonify({'res':'failed','error':str(e)})



    def train_info(self,action_name,dic):
    # print('162----',action_name,dic)
        if action_name:
            return dic['by_action_name'][action_name]
        else:
            return ['','','']

    
    def write_cls_tkn(self,dic_tkn):
        # print(dic_tkn)
        try:
            fn=os.path.join(self.config_lz['work_dir'],'01-会员管理','会员资料',dic_tkn['cus_name']+'.xlsm')
            df_tkn=pd.DataFrame(dic_tkn,index=[0])
            df_tkn=df_tkn[['cls_tkn_date','cls_tkn_time','cls_long','cls_type','ins_name','basic_cls_comment']]
            df_old=pd.read_excel(fn,sheet_name='上课记录')


            app=xw.App(visible=False)
            wb=app.books.open(fn)
            sht=wb.sheets['上课记录']
            row = df_old.shape[0]+2
            rng='A'+str(row)+':F'+str(row)
            sht.range(rng).value=df_tkn.iloc[0].tolist()

            wb.save(fn)
            wb.close()
            app.quit()
            return '\n写入上课表成功'
        except Exception as e:
            return  f'写入上课表错误： {e}'
    
        
    def write_train_rec(self,dic):
        # dic=request.json
        trainlist=self.get_train_dic()
        # print(trainlist)
        basic_cls_info=dic['cls_tkn']
        print(dic['train_rec']['train_recs'])
        cus_name,ins_name=basic_cls_info['cus_name'],basic_cls_info['ins_name']
        # print(dic)
        fn=os.path.join(self.config_lz['work_dir'],'01-会员管理','会员资料',cus_name+'.xlsm')
        # print(dic['train_rec'])
    
        
        df_train_rec=pd.DataFrame(dic['train_rec']['train_recs'])
        # print(df_train_rec)
        #'nonOxyWt','nonOxyDis','nonOxyNum','nonOxyGroup','oxyTime','oxyGroup','calories'
        df_train_rec['nonOxyGroup']=df_train_rec['nonOxyGroup'].replace('','1')
        df_train_rec['oxyGroup']=df_train_rec['oxyGroup'].replace('','1')
        df_train_rec['oxyTime']=df_train_rec['oxyTime'].replace('','0')
        df_train_rec['nonOxyWt']=df_train_rec['nonOxyWt'].replace('','0')
        df_train_rec['nonOxyDis']=df_train_rec['nonOxyDis'].replace('','0')
        df_train_rec['nonOxyNum']=df_train_rec['nonOxyNum'].replace('','0')
        # df_train_rec=df_train_rec[['cls_tkn_date','cls_tkn_time','cls_long','cls_type','ins_name','basic_cls_comment']]
        # df_old=pd.read_excel(fn,sheet_name='训练情况')
        try:
            df_copy_rows_nonOxy= df_train_rec.loc[df_train_rec.index.repeat(df_train_rec['nonOxyGroup'].astype(int))]
        except Exception as e:
            print(f'\n写入训练记录表错误：{e}')
            return f'\n写入训练记录表错误：{e}'
        try:
            df_copy_rows= df_copy_rows_nonOxy.loc[df_train_rec.index.repeat(df_train_rec['oxyGroup'].astype(int))]
        except Exception as e:
            print(f'\n写入训练记录表错误：{e}')
            return f'\n写入训练记录表错误：{e}'
        
        df_copy_rows.reset_index(drop=True, inplace=True)

        df_copy_rows['date']=dic['train_rec']['trainDate']
        df_copy_rows['calories']=dic['train_rec']['calories']
        df_copy_rows['train_comment']=dic['train_rec']['trainComment']
        df_copy_rows['search_name']=df_copy_rows['nonOxyName']+df_copy_rows['oxyName']

        # print(df_copy_rows)

        try:
            df_copy_rows['big_type']=df_copy_rows['search_name'].apply(lambda x: self.train_info(x,trainlist)[0])
            df_copy_rows['muscle']=df_copy_rows['search_name'].apply(lambda x: self.train_info(x,trainlist)[1]+'肌群')
        except Exception as e:
            print('从动作名称获取动作大类及肌肉部位错误：',e)
            return '从动作名称获取动作大类及肌肉部位错误：:'+e
        df_copy_rows.loc[df_copy_rows['big_type'] == '有氧训练', 'muscle'] = ''
        # print(df_copy_rows)

        to_int_list=['nonOxyNum','nonOxyGroup','oxyGroup']
        to_float_list=['oxyTime','nonOxyWt','nonOxyDis','calories']
        for itm in to_int_list:
            try:
                df_copy_rows[itm]=df_copy_rows[itm].fillna(0)
                df_copy_rows[itm]=df_copy_rows[itm].astype(int)
            except Exception as e:
                return 'err:'+e
        for itm in to_float_list:
            try:
                df_copy_rows[itm]=df_copy_rows[itm].fillna(0)
                df_copy_rows[itm]=df_copy_rows[itm].astype(float)
            except Exception as e:
                return 'err:'+e
        try:
            df_copy_rows['oxyTime']=df_copy_rows['oxyTime'].apply(lambda x: int(x)*60)
        except Exception as e:
            return 'err:'+e

        df_copy_rows.replace(0,'',inplace=True)
        df_train_recs=df_copy_rows[['date','big_type','muscle','oxyName','oxyTime','nonOxyName','nonOxyWt','nonOxyDis','nonOxyNum','calories','train_comment']]
        df_train_recs_copy=df_train_recs.copy()
        df_train_recs_copy['ins_name']=ins_name
        df_write_train_rec=df_train_recs_copy[['date','big_type','muscle','oxyName','oxyTime','nonOxyName','nonOxyWt','nonOxyDis','nonOxyNum','calories','ins_name','train_comment']]
        # print(df_write_train_rec)

        df_old=pd.read_excel(fn,sheet_name='训练情况')
        app=xw.App(visible=False)
        wb=app.books.open(fn)
        sht=wb.sheets['训练情况']
        row_start = df_old.shape[0]+2
        row_end=row_start+df_write_train_rec.shape[0]-1
        # rng='A'+str(row_start)+':J'+str(row_end)
        start_cell='A'+str(row_start)
        # sht.range(rng).value=df_old.tolist()
        sht.range(start_cell).options(index=False, header=False).value=df_write_train_rec

        wb.save(fn)
        wb.close()
        app.quit()

        return f'\n写入训练记录表成功'

    def wecom_dir(self):
        # fn=os.path.join(os.path.dirname(__file__),'config','wecom_dir.config')        
        res=os.path.join(self.config_lz['work_dir'].strip(),'01-会员管理','会员资料')
        return res

    def get_cus_buy(self):
        cus_name=request.data.decode('utf-8')
        # print(cus_name)
        
        # print(fn)
        try:
            fn=os.path.join(self.config_lz['work_dir'],'01-会员管理','会员资料',cus_name.strip()+'.xlsm')
            df=pd.read_excel(fn,sheet_name='购课表')
            df['收款次数']=1
            # df_gp=df.groupby('购课编码')
            df_res = df.groupby('购课编码').agg({
            '应收金额': 'mean',
            '实收金额': 'sum',
            '购课类型': 'first',
            '收款日期': lambda x: '\n'.join(x.dt.strftime('%Y/%m/%d')),
            '收款次数': 'count'
        }).reset_index()
            df_res['未收金额']=df_res['应收金额']-df_res['实收金额']

            df_res=df_res[['购课编码','购课类型','应收金额','实收金额','未收金额','收款次数','收款日期']]
            # df_res.reset_index(drop=True,inplace=True)
            j_data={}
            for key ,value in df_res.to_dict(orient='index').items():
                j_data[key]=list(value.values())
            # result = {str(i): {key: value[i] for key, value in df_res.items()} for i in range(len(df_res['购课编码']))}
            return jsonify(j_data)
            # return jsonify(j_data)

        except Exception as err:
            return {'dat':'获取客户购课错误：','error':err}      
   
    def get_cus_buy_db(self):
        # cus_name=request.data.decode('utf-8')
        print('from llz database,get_cus_buy_statistics')
        # cus_id_name=request.data.decode('utf-8')
        data=request.json
        cus_id_name=data['cusIdName']
        # cus_id_name='MH00113肖婕'
        cus_id,cus_name=cus_id_name[:7],cus_id_name[7:]
        print(cus_id)

        conn=self.connect_mysql()
        cursor=conn.cursor()
        # sql=f'''SELECT 
        #             buy_flow_id, 
        #             avg(pay) AS 总应收金额,
        #             SUM(real_pay) AS 总实收金额,
        #             concat(min(buy_type),'-',min(buy_cls_name)) AS 购课类型,
        #             GROUP_CONCAT(DISTINCT DATE_FORMAT(buy_date, '%Y/%m/%d') ORDER BY buy_date ASC SEPARATOR '\n') AS 收款日期列表,
        #             COUNT(*) AS 收款次数
                    
        #         FROM
        #             buy_rec_table
        #         WHERE
        #             cus_id='{cus_id}'
        #         GROUP BY
        #             buy_flow_id;
        # '''
        # cursor.execute(sql)
        # buy_stat=cursor.fetchall()

        sql='''
            SELECT 
                brt.buy_flow_id, 
                avg(pay) AS 总应收金额,
                SUM(real_pay) AS 总实收金额,
                concat(min(buy_type),'-',min(buy_cls_name)) AS 购课类型,
                GROUP_CONCAT(DISTINCT DATE_FORMAT(buy_date, '%%Y/%%m/%%d') ORDER BY buy_date ASC SEPARATOR '\n') AS 收款日期列表,
                COUNT(*) AS 收款次数,
                COALESCE(MIN(vurt_vct.vc_id),'-') as 优惠券号,
                COALESCE(MIN(vurt_vct.amount),'-') as 优惠额度

            FROM
                buy_rec_table brt	
            LEFT JOIN 
                (
                    SELECT vct.vc_id as vc_id,vct.amount as amount,vurt.buy_flow_id as buy_flow_id from vc_cards_table vct LEFT JOIN vc_use_rec_table vurt ON vurt.vc_id=vct.vc_id
                ) AS vurt_vct ON vurt_vct.buy_flow_id=brt.buy_flow_id

            WHERE
                brt.cus_id=%s
            GROUP BY
                brt.buy_flow_id;
        '''
        cursor.execute(sql,(cus_id,))
        buy_stat=cursor.fetchall()

        if buy_stat:
            df=pd.DataFrame(buy_stat)
            df.columns=['购课编码','应收金额','实收金额','购课类型','收款日期','收款次数','优惠券号','优惠额度']
            df['应收金额']=df['应收金额'].astype(float)
            df['实收金额']=df['实收金额'].astype(float)
            df['未收金额']=df['应收金额']-df['实收金额']
            df=df[['购课编码','购课类型','应收金额','实收金额','未收金额','收款次数','收款日期','优惠券号','优惠额度']]

            buy_stat_list=[row.tolist() for row in df.values]        
            return jsonify({'buy_stat_list':buy_stat_list})
        else:
            return jsonify({'buy_stat_list':None})

    def get_cus_buy_list(self,cus_name):
        # cus_name=request.data.decode('utf-8')
        fn=os.path.join(self.config_lz['work_dir'],'01-会员管理','会员资料',cus_name.strip()+'.xlsm')
        df=pd.read_excel(fn,sheet_name='购课表')
        # dic_buy=df.to_dict()
        # dic_res=self.dic_format(dic=dic_buy,order_name='收款日期')
        # return jsonify(dic_res)
        return df

    def  get_limit_class_records(self,cus_name):
        fn=os.path.join(self.config_lz['work_dir'],'01-会员管理','会员资料',cus_name.strip()+'.xlsm')
        df=pd.read_excel(fn,sheet_name='限时课程记录')

        df.fillna('',inplace=True)
        return df

    def get_not_start_lmt_list(self,cus_name):
        # cus_name=request.data.decode('utf-8')
        fn=os.path.join(self.config_lz['work_dir'],'01-会员管理','会员资料',cus_name.strip()+'.xlsm')
        df=pd.read_excel(fn,sheet_name='辅助表')
        df_not_start=df['未开课的购课编码']
        df_not_start.dropna(inplace=True)
        df_not_start=df_not_start.to_frame()
        # dic_not_start=df_not_start.to_dict()
        # print(df_not_start,dic_not_start)
        # return jsonify(dic_not_start)
        return df_not_start

    def deal_start_limit_page(self):
        cus_name_input=request.data.decode('utf-8')
        print('deal_start_limit_page:',cus_name_input)
        try:
            # 获取购课表
            df_buy=self.get_cus_buy_list(cus_name=cus_name_input)
            # print(df_buy)
            if df_buy.empty:
                dic_buy=''
            else:
                df_buy.fillna('',inplace=True)
                dic_buy=df_buy.to_dict()
                dic_buy=self.dic_format(dic=dic_buy,order_name='收款日期')
            

            # 获取限时课程记录、目前生效的限时课程记录
            df_limit_cls_recs=self.get_limit_class_records(cus_name=cus_name_input)
            # pd无法直接将NaT替换为空值，将其替换为0后，再转为''
            df_limit_cls_recs.fillna(0, inplace=True)
            df_limit_cls_recs.replace(0,'',inplace=True)     

            if df_limit_cls_recs.empty:
                dic_limit_cls_recs=''
                dic_limit_maxdate_rec={'购课编码':'','限时课程起始日':'','限时课程结束日':'','限时课程实际结束日':''}
                dic_limit_maxdate_rec={'0':dic_limit_maxdate_rec}
            else:   
                df_limit_maxdate_rec=df_limit_cls_recs[df_limit_cls_recs['限时课程结束日']==df_limit_cls_recs['限时课程结束日'].max()]
                # df_limit_maxdate_rec.fillna('',inplace=True)
                df_limit_maxdate_rec.reset_index(inplace=True)
                # print('df_limit_maxdate_rec:',df_limit_maxdate_rec)

                dic_limit_cls_recs=df_limit_cls_recs.to_dict()
                dic_limit_cls_recs=self.dic_format(dic=dic_limit_cls_recs,order_name='购课编码')


                dic_limit_maxdate_rec=df_limit_maxdate_rec.to_dict()
                dic_limit_maxdate_rec=self.dic_format(dic=dic_limit_maxdate_rec,order_name='购课编码')
            # print('df limit cls recs, df limit maxdate rec: \n',df_limit_cls_recs,df_limit_maxdate_rec) 
            # print('dic_limit_maxdate_rec:',dic_limit_cls_recs)

            # 获取并计算未开课的限时课程表，包括限时私教和团课
            df_buy_limit = df_buy[df_buy['购课类型'].isin(['限时课程', '限时团课'])]
            df_not_start=self.get_not_start_lmt_list(cus_name=cus_name_input)
            df_not_start.fillna('',inplace=True)
            # print(type(df_not_start),df_not_start)
            df_not_start.rename(columns={'未开课的购课编码':'购课编码'},inplace=True)
            if df_not_start.empty:            
                df_merge=pd.DataFrame(data={'收款日期':'','购课编码':'','购课类型':'','购课节数':'','购课时长（天）':'',
                                            '应收金额':'','实收金额':'','收款人':'','收入类别':'','备注':''},index=[0])
                # df_merge=df_buy_limit                      
            else:
                df_merge=pd.merge(df_buy_limit,df_not_start,on='购课编码',how='inner')  
    
            # print('\n471 line',df_merge)
            df_merge.reset_index(inplace=True)
            # print('deal_start_limit_page()',df_merge)

            if df_merge.empty:
                dic_not_start=''
            else:      
                df_merge.fillna('',inplace=True)
                df_merge.sort_values(by='收款日期',ascending=True,inplace=True)
                dic_not_start=df_merge.to_dict()
                # print('\n479 line dic_not_start',dic_not_start)
                dic_not_start=self.dic_format(dic=dic_not_start,order_name='收款日期')
            
            return jsonify({'not_start_list':dic_not_start,'buy_list':dic_buy,'limit_cls_recs':dic_limit_cls_recs,'maxdate_limit_class_rec':dic_limit_maxdate_rec})
        except Exception as e:
            return jsonify({'error':e,'not_start_list':dic_not_start,'buy_list':dic_buy,'limit_cls_recs':dic_limit_cls_recs,'maxdate_limit_class_rec':dic_limit_maxdate_rec})




    def send_to_start_lmt_page_data(self):
        data=request.json
        cus_id_name=data['cus_name']
        cus_id,cus_name=cus_id_name[:7],cus_id_name[7:]
        conn=self.connect_mysql() 
        cursor=conn.cursor()

        #获取限时课程列表
        sql="select cls_name from cls_type_table where cls_type='限时课程'"
        cursor.execute(sql)

        #获取并整理课程类型
        _all_lmt_names=cursor.fetchall()
        all_lmt_names=[item[0] for item in _all_lmt_names]

        lmt_cls_infos=Vividict()

        try:
            for lmt_cls_name in all_lmt_names:
                
            #未开启限时课程信息
                # sql='''
                #     select DISTINCT card_id,card_start_time,prd,end_time,cls_qty from cards_table where card_id in 
                #     (select card_id from cardholder_card_table where cus_id=%s)
                #     and card_type=%s 
                #     and card_name=%s
                #     and card_start_time is NULL
                #     and end_time is NULL
                # '''

                sql='''
                    select DISTINCT card_id,max(card_start_time) as card_start_time,prd,max(end_time) as end_time,cls_qty,valid_time from cards_table where card_id in 
                    (select card_id from cardholder_card_table where cus_id=%s)
                    and card_type=%s
                    and card_name=%s
                    group by card_id,prd,cls_qty,valid_time
                    having card_start_time is null and end_time is null;   
                '''
                cursor.execute(sql,(cus_id,'限时课程',lmt_cls_name))
                not_start_limit_recs=cursor.fetchall()
                # print(not_start_limit_recs)

                #当天有效的限时课程信息
                current_day=datetime.datetime.now().strftime('%Y-%m-%d')
                # print('send to start lmt page data: current_day value:',current_day)
                # sql='''
                #     select DISTINCT card_id,card_start_time,prd,end_time from cards_table where card_id in 
                #     (select card_id from cardholder_card_table where cus_id=%s)
                #     and card_type=%s 
                #     and card_name=%s
                #     and card_start_time<=%s
                #     and end_time>=%s
                # '''

                # 延长课程时，为insert，对于同一个card_id，查询opr_time为最大的那条记录，并且剩余课程节数>0
                sql='''
                    SELECT DISTINCT ct.card_id, ct.card_start_time, ct.prd, ct.end_time,ct.cls_qty,COALESCE(ctrt.cls_qty,0) as cls_tkn_qty, COALESCE(ct.cls_qty,0)-COALESCE(ctrt.cls_qty,0) as cls_remain ,ct.valid_time
                    FROM cards_table ct
                    JOIN (
                        SELECT cht.card_id, MAX(ct.opr_time) AS max_opr_time
                        FROM cardholder_card_table cht
                        JOIN cards_table ct ON cht.card_id = ct.card_id
                        WHERE cht.cus_id = %s
                        GROUP BY cht.card_id
                        ) AS subq ON ct.card_id = subq.card_id AND ct.opr_time = subq.max_opr_time
                    JOIN(
                    	SELECT card_id, sum(cls_long) as cls_qty from cls_tkn_rec_table 
                        group by card_id
                    	
                    	) as ctrt on ctrt.card_id=ct.card_id
                        
                    WHERE ct.card_id IN (
                        SELECT card_id
                        FROM cardholder_card_table
                        WHERE cus_id =%s
                        )
                    AND ct.card_type = %s
                    AND ct.card_name =  %s
                    AND ct.card_start_time <= %s
                    AND ct.end_time > %s
                    AND COALESCE(ct.cls_qty, 0) - COALESCE(ctrt.cls_qty, 0) > 0;
                '''
                cursor.execute(sql,(cus_id,cus_id,'限时课程',lmt_cls_name,current_day,current_day))
                current_valid_limit_rec=cursor.fetchall()

                lmt_cls_infos[lmt_cls_name]['not_start_limit_recs']=not_start_limit_recs
                lmt_cls_infos[lmt_cls_name]['current_valid_limit_rec']=current_valid_limit_rec

            # print(lmt_cls_infos)

            return jsonify({'res':'ok',
                            'lmt_cls_infos':lmt_cls_infos})
        except Exception as e:
            print('get limit recs error in send_to_start_lmt_page_data() :',e)
            return jsonify({'res':'failed'})
        finally:
            cursor.close()
            conn.close()





        # try:
        #     #未开启限时课程信息
        #     sql='''
        #         select DISTINCT card_id,card_start_time,prd,end_time,cls_qty from cards_table where card_id in 
        #         (select card_id from cardholder_card_table where cus_id=%s)
        #         and card_type=%s 
        #         and card_name=%s
        #         and card_start_time is NULL
        #         and end_time is NULL
        #     '''
        #     cursor.execute(sql,(cus_id,'限时课程','私教'))
        #     not_start_limit_recs=cursor.fetchall()
        #     # print(not_start_limit_recs)

        #     #当天有效的限时课程信息
        #     current_day=datetime.datetime.now().strftime('%Y-%m-%d')
        #     # sql='''
        #     #     select DISTINCT card_id,card_start_time,prd,end_time from cards_table where card_id in 
        #     #     (select card_id from cardholder_card_table where cus_id=%s)
        #     #     and card_type=%s 
        #     #     and card_name=%s
        #     #     and card_start_time<=%s
        #     #     and end_time>=%s
        #     # '''

        #     # 延长课程时，为insert，对于同一个card_id，查询opr_time为最大的那条记录    
        #     sql='''
        #         SELECT DISTINCT ct.card_id, ct.card_start_time, ct.prd, ct.end_time,ct.cls_qty
        #         FROM cards_table ct
        #         JOIN (
        #             SELECT cht.card_id, MAX(ct.opr_time) AS max_opr_time
        #             FROM cardholder_card_table cht
        #             JOIN cards_table ct ON cht.card_id = ct.card_id
        #             WHERE cht.cus_id = %s
        #             GROUP BY cht.card_id
        #             ) AS subq ON ct.card_id = subq.card_id AND ct.opr_time = subq.max_opr_time
        #         WHERE ct.card_id IN (
        #             SELECT card_id
        #             FROM cardholder_card_table
        #             WHERE cus_id = %s
        #             )
        #         AND ct.card_type = %s 
        #         AND ct.card_name = %s
        #         AND ct.card_start_time <= %s
        #         AND ct.end_time >= %s
        #     '''
        #     cursor.execute(sql,(cus_id,cus_id,'限时课程','私教',current_day,current_day))
        #     current_valid_limit_rec=cursor.fetchall()


        #     return jsonify({'res':'ok',
        #                     'not_start_limit_recs':not_start_limit_recs,
        #                     'current_valid_limit_rec':current_valid_limit_rec})
        # except Exception as e:
        #     print('get limit recs error')
        #     return jsonify({'res':'failed'})
        # finally:
        #     cursor.close()
        #     conn.close()

    #获取常规课程信息
    def c_cls(self,cus_id,cls_type,cls_name,cls_tkn_time):
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()

            sql='''
                SELECT 
                    filtered_a.card_id, 
                    MAX(cards_table.end_time) AS end_time,
                    filtered_a.cls_qty - IFNULL(b_count.times, 0) AS remain_qty,
                    filtered_a.card_name,
                    filtered_a.valid_time
                FROM (
                    SELECT ct.card_id, ct.card_name, ct.cls_qty,ct.id,ct.valid_time
                    FROM cards_table ct
                    INNER JOIN (
                        SELECT card_id, MAX(id) AS id
                        FROM cards_table
                        WHERE card_id IN (
                            SELECT card_id
                            FROM cardholder_card_table
                            WHERE cus_id = %s
                        )
                        GROUP BY card_id
                    ) max_ct 
                    ON ct.card_id = max_ct.card_id AND ct.id=max_ct.id
                    WHERE ct.card_type = %s
                    AND ct.card_name = %s
                    AND ct.end_time >= %s
                ) AS filtered_a
                LEFT JOIN (
                    SELECT card_id, count(card_id) AS times
                    FROM cls_tkn_rec_table
                    GROUP BY card_id
                ) AS b_count 
                ON filtered_a.card_id = b_count.card_id
                LEFT JOIN cards_table 
                ON filtered_a.card_id = cards_table.card_id
                GROUP BY filtered_a.card_id, filtered_a.card_name, filtered_a.cls_qty,filtered_a.valid_time
                HAVING remain_qty > 0;
            '''
            cursor.execute(sql,(cus_id,cls_type,cls_name,cls_tkn_time))
            orgin_res=cursor.fetchall()  
            # print('orgin_res:',orgin_res,'\ncls type:',cls_type,'\ncls_name:',cls_name)
            res = [item for item in orgin_res]
            # print('res:',res)
            return res
        except Exception as e:
            print(f'get {cls_type} {cls_name} error...',e)
            return None
        finally:
            cursor.close()
            conn.close()

    #获取限时课程的信息
    def l_cls(self,cus_id,cls_type,cls_name,cls_tkn_time):
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()

            sql='''
                SELECT 
                    filtered_a.card_id, 
                    MAX(cards_table.end_time) AS end_time,
                    MIN(filtered_a.cls_qty) - IFNULL(b_count.times, 0) AS remain_qty,
                    filtered_a.card_name,
                    filtered_a.valid_time
                FROM (
                    SELECT card_id, card_name, cls_qty, valid_time
                    FROM cards_table
                    WHERE (card_id, id) IN (
                        SELECT card_id, MAX(id) AS id
                        FROM cards_table
                        WHERE card_id IN (
                            SELECT card_id
                            FROM cardholder_card_table
                            WHERE cus_id=%s
                        )
                        AND card_type=%s
                        AND card_name=%s
                        AND end_time >= %s
                        AND card_start_time <= %s
                        GROUP BY card_id
                    )
                ) AS filtered_a
                LEFT JOIN (
                    SELECT card_id, sum(cls_long) AS times
                    FROM cls_tkn_rec_table
                    GROUP BY card_id
                ) AS b_count ON filtered_a.card_id = b_count.card_id 
                LEFT JOIN cards_table ON filtered_a.card_id = cards_table.card_id
                GROUP BY filtered_a.card_id,filtered_a.card_name,filtered_a.valid_time
                HAVING remain_qty > 0;

                '''                                 
            cursor.execute(sql,(cus_id,cls_type,cls_name,cls_tkn_time,cls_tkn_time))
            orgin_res=cursor.fetchall()  
            res = [item for item in orgin_res]
            return res
        except Exception as e:
            print(f'get {cls_type} {cls_name} error...',e)
            return None
        finally:
            cursor.close()
            conn.close()

    def get_cus_cls_infos_db(self):
        print('get buy history via get_cus_cls_infos_db()')
        data=request.json
        cus_id_name=data['cus_name']
        # cus_cls_input和input_buy页面需要这个函数，但input_buy进来的数据没有cls_tkn_time
        try:
            cls_tkn_time=datetime.datetime.strptime(data['cls_tkn_time'],'%Y-%m-%dT%H:%M')
        except:
            cls_tkn_time=''
        cus_id,cus_name=cus_id_name[:7],cus_id_name[7:]

        conn=self.connect_mysql() 
        cursor=conn.cursor()

        try:
            #获取所有卡的类型名称
            sql='select cls_type,cls_name,cls_letter from cls_type_table'
            cursor.execute(sql)
            all_cls_types=cursor.fetchall()         
        except Exception as e:
            print('get cls types from cls_type_table error',e)
            all_cls_types=None


        #遍历所有卡的类型并获取有关的课程信息
        cls_infos={}
        lmt_cls=[]
        long_cls=[]
        for cp in all_cls_types:
            if cp[0]=='限时课程':
                res_lmt=self.l_cls(cus_id=cus_id,cls_type=cp[0],cls_name=cp[1],cls_tkn_time=cls_tkn_time)
                if res_lmt:
                    lmt_cls.extend(res_lmt)
    
            
            if cp[0]=='常规课程':
                res_long=self.c_cls(cus_id=cus_id,cls_type=cp[0],cls_name=cp[1],cls_tkn_time=cls_tkn_time)
                if res_long:
                    long_cls.extend(res_long)
        
        cls_infos['限时课程']=lmt_cls
        cls_infos['常规课程']=long_cls

        # print('cls infos:',cls_infos)

        try:
            sql='''
            select cus_id,cus_name,buy_date,card_id,buy_flow_id,buy_type,buy_cls_name,buy_num,buy_cls_days,pay,real_pay,cashier_id,income_type,comment  
            from buy_rec_table 
            where cus_id=%s;
            '''
            cursor.execute(sql,cus_id)
            buy_list=cursor.fetchall()
            if buy_list:
                buy_list=self.convert_mysql_data_to_string(buy_list)
                # buy_rec_cols=['id','cus_id','cus_name','buy_date','buy_code','buy_type','buy_num','buy_cls_days','pay','real_pay','cashier_name','income_type','comment']
                buy_list_cols=['cus_id','cus_name','收款日期','购课编码','购课流水号','购课类型','课程名称','购课节数','购课时长（天）','应收金额','实收金额','收款人','收入类别','备注']
                buy_list=self.mysql_list_data_to_dic(data=buy_list,mysql_cols=buy_list_cols)  
            else:
                buy_list={'cus_id':'','cus_name':'','收款日期':'','购课编码':'','购课流水号':'','购课类型':'','课程名称':'','购课节数':'','购课时长（天）':'',
                                                '应收金额':'','实收金额':'','收款人':'','收入类别':'','备注':''}   
            conn.close()
            cursor.close()
        except Exception as e:
            print('get buy list error, set buy_list None :',e)   
            conn.close()
            cursor.close()
            buy_list=None


        return jsonify({'buy_list':buy_list,
                        'cus_cls_infos':cls_infos
                        })

    def convert_mysql_data_to_string(self,data,method=1):        
        if method==1:
            converted_data = []
            for item in data:
                if isinstance(item, tuple) or isinstance(item, list):
                    converted_data.append(self.convert_mysql_data_to_string(item))
                elif isinstance(item, datetime.date):
                    converted_data.append(item.strftime('%Y-%m-%d'))
                elif isinstance(item, Decimal):
                    converted_data.append(float(item))
                else:
                    converted_data.append(item)
        elif method==2:
            # 用于既往购课次数、未收金额的统计,输入的是字典
            converted_data = {}
            for key,item in data.items():
                if isinstance(item, tuple) or isinstance(item, list) or isinstance(item, dict):
                    converted_data[key]=self.convert_mysql_data_to_string(item,method=2)
                elif isinstance(item, datetime.date):
                    converted_data[key]=item.strftime('%Y-%m-%d')
                elif isinstance(item, Decimal):
                    converted_data[key]=float(item)
                else:
                    converted_data[key]=item
        
        return converted_data

    def mysql_list_data_to_dic(self,data,mysql_cols):
        result={}
        for key,item in enumerate(data):
            result[key]=self.list_to_dict(item,column_list=mysql_cols)

        return result

    def list_to_dict(self,data,column_list):
        result={}
        for key,item in enumerate(column_list):
            result[item]=data[key]
        
        return result

    def read_template(self):
        df=pd.read_excel(os.path.join(self.config_lz['work_dir'],'01-会员管理','模板.xlsm'),sheet_name='辅助表')
        df_cls_types=df[['购课类型']].copy().dropna()
        cls_types=df_cls_types['购课类型'].tolist()
        
        df_cashier=df[['收款人']].copy().dropna()
        cashier=df_cashier['收款人'].tolist()

        df_income_types=df[['收入类别']].copy().dropna()
        income_types=df_income_types['收入类别'].tolist()

        data={'cls_types':cls_types,'cashiers':cashier,'income_types':income_types}
        return data

    def write_buy_date_to_trial_table(self,formal_cus_name,first_buy_date):
        print('写入体验表：',formal_cus_name,first_buy_date)
        try:
            fn=os.path.join(self.config_lz['work_dir'],'03-教练管理','体验课上课记录表.xlsx')
            app=xw.App(visible=False)
            wb=app.books.open(fn)
            sht=wb.sheets['体验课上课记录表']
            m_column_data=sht.range('M:M').value
            # 从M列找到 出单后正式会员编码及姓名
            for row_index, value in enumerate(m_column_data, start=1):
                if value == formal_cus_name:
                    print(value,first_buy_date)
                    # 根据条件设置 M 列的值为 'AA'
                    if sht.range(f'L{row_index}').value is None:
                        sht.range(f'L{row_index}').value = first_buy_date
                        sht.range(f'K{row_index}').value = '是'

            wb.save(fn)
            wb.close()
            app.quit()
        except Exception as e:
            print('写入体验课表错误：',e)
            return '写入体验课表错误：'+e
    
    def write_buy(self):
        wk_dir=self.config_lz['work_dir']
        dat=request.json
        for key,value in dat.items():
            try:
                dat[key]=int(value)
            except:
                pass
        fn=os.path.join(wk_dir,'01-会员管理','会员资料',dat['客户编码及姓名'].strip()+'.xlsm')

        df=pd.DataFrame(dat,index=[0])
        df=df[['收款日期','客户购课编号','购课类型','购课节数','购课时长（天）','应收金额','实收金额','收款人','收入类别','备注']]
        df_old=pd.read_excel(fn,sheet_name='购课表')

        app=xw.App(visible=False)
        wb=app.books.open(fn)
        sht=wb.sheets['购课表']
        row = df_old.shape[0]+2
        rng='A'+str(row)+':J'+str(row)
        sht.range(rng).value=df.iloc[0].tolist()

        wb.save(fn)
        wb.close()
        app.quit()
        

        if dat['购课类型'].strip() in ['限时课程','限时团课']:
            aux_res=self.add_rec_in_aux_table(dat['客户购课编号'])
        else:
            aux_res='非限时课程'


        # 写入体检课表的出单日期
        print(df['客户购课编号'].tolist()[0][:-8],df['收款日期'].tolist()[0])
        self.write_buy_date_to_trial_table(formal_cus_name=df['客户购课编号'].tolist()[0][:-8],first_buy_date=df['收款日期'].tolist()[0])

        return f'写入成功, 行号：{row}, {aux_res}'

    def write_buy_db(self):
        # 规则：
        # 限时卡每人可以有多张
        # 普通卡每人仅可以有一张，在前端通过get_cus_cards_by_type()查询已有卡号，
        # get_cus_cards_by_type()中，如果已有卡号，则用旧卡号，否则生成新卡号返回前端。
        try:
            wk_dir=self.config_lz['work_dir']
            dat=request.json
            print('input buy data:',dat)
            dat['cus_id']=dat['客户编码及姓名'][:7].strip()
            dat['cus_name']=dat['客户编码及姓名'][7:].strip()
            
            del dat['客户编码及姓名']
            data_cols=['cus_id','cus_name','收款日期', '购课卡号','购课流水号','购课类型','购课名称','购课节数', '购课时长（天）','有效上课时段','应收金额', '实收金额','与应收一致', '收款人', '收入类别', '备注','管理员备注','operatorId','operateTime']
            sorted_data={key: dat[key] for key in data_cols}
            values_buy_table=tuple(sorted_data.values())

            conn=self.connect_mysql()
            cursor=conn.cursor()
            
            conn.begin()


            #写入卡表          
            if dat['购课类型']=='限时课程':
                #查询表中是否已有购课流水，如有，说明为补交余款，获取原来相应的起始和终止日
                sql='''select distinct card_start_time, end_time from cards_table 
                    where card_id in 
                        (select card_id from buy_rec_table where buy_flow_id=%s)
                    and card_start_time is not null
                    and end_time is not null
                '''
                try:
                    cursor.execute(sql,sorted_data['购课流水号'])
                    existed_flow_id=cursor.fetchall()
                    if(existed_flow_id):
                        s_time,end_time=existed_flow_id[0]
                    else:
                        s_time=None
                        end_time=None
                except:
                    pass

                
            elif dat['购课类型']=='常规课程' :
                #查询表中是否已有购课流水，如有，说明为补交余款，获取原来相应的起始和终止日
                sql='''select distinct card_start_time, end_time from cards_table 
                    where card_id in 
                        (select card_id from buy_rec_table where buy_flow_id=%s)
                    and card_start_time is not null
                    and end_time is not null
                '''
                try:
                    cursor.execute(sql,sorted_data['购课流水号'])
                    existed_flow_id=cursor.fetchall()
                    print('existed_flow_id: ',existed_flow_id,'\ninput_flow_id:',sorted_data['购课流水号'])
                    if(existed_flow_id):
                        
                        s_time,end_time=existed_flow_id[0]
                    else:

                        s_time=dat['收款日期']
                        e_time=datetime.datetime.strptime(s_time,'%Y-%m-%d')+datetime.timedelta(days=int(dat['购课时长（天）']))
                        end_time=e_time.strftime('%Y-%m-%d')
                except:
                    pass
            
            sql='''
                insert into cards_table
                (card_id,card_type,card_name,cls_qty,card_start_time,prd,valid_time,end_time,cmt,opr_id,opr_time)
                values
                (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
            '''

            values_cards_table=(dat['购课卡号'],dat['购课类型'],
                        dat['购课名称'],dat['购课节数'],
                        s_time,dat['购课时长（天）'],dat['有效上课时段'],end_time,dat['备注'],dat['operatorId'],dat['operateTime'])
            cursor.execute(sql,values_cards_table)

            #写入购课表
            sql='''
                insert into buy_rec_table
                (cus_id,cus_name,buy_date,card_id,buy_flow_id,buy_type,buy_cls_name,buy_num,buy_cls_days,valid_time,pay,real_pay,cons_with_pay,cashier_id,income_type,comment,adm_cmt,operator_id,operate_time)
                values
                (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
            '''
            cursor.execute(sql,values_buy_table)
            
            

            #写入持卡人-卡表
            sql='''
                select card_id from cardholder_card_table
                where cus_id=%s
            '''
            cursor.execute(sql,dat['cus_id'])
            hasIds=cursor.fetchall()
            has_ids=[]
            for id in hasIds:
                if id[0] not in has_ids:
                    has_ids.append(id[0])
            print('hasIds:',has_ids)
            if dat['cus_id'] in has_ids:
                pass
            else:
                try:
                    values_holder_card_table=(dat['cus_id'],dat['购课卡号'],datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),'')
                    sql='''
                        insert into cardholder_card_table 
                        (cus_id,card_id,relation_time,cmt)
                        values
                        (%s,%s,%s,%s)
                    '''
                    cursor.execute(sql,values_holder_card_table)
                except:
                    print('insert into cardholder_card_table error')
                    raise FERROR 


            conn.commit()

            
            cus_name=sorted_data['cus_name']
            res_txt=f'{cus_name} 增加一条购课记录'

            # if sorted_data['购课类型'] in ['限时课程','限时团课']:
            #     try:
            #         sql=f'''
            #             insert into not_start_lmt_table
            #             (cus_id,cus_name,buy_code)
            #             values
            #             (%s,%s,%s)
            #         '''
            #         cursor.execute(sql,[sorted_data['cus_id'],sorted_data['cus_name'],sorted_data['客户购课编号']])
            #         res_txt=f'; {cus_name} 未开课的限时课程记录增加成功'
            #     except Exception as start_lmt_error:                    
            #         res_txt=f' \n{cus_name} 未开课限时课程记录未增加成功:\n可能是同一天购课导致的重复购课编码。'
            #         print(f'ERROR: {cus_name} 未开课限时课程记录未增加成功：{start_lmt_error}')
            #         raise FERROR('未开课限时课程记录未增加成功，可能是同一天购课导致的重复购课编码。')
            
            
            
            
            return jsonify({'res':'ok','msg':res_txt})
        except Exception as e:
            conn.rollback()
            print('wirte_buy_db() error, rollbacked!', e)
            return jsonify({'res':'failed','msg':res_txt})
        finally:
            cursor.close()
            conn.close()


    def cus_list(self):
        dic_li=[]
        for fn in os.listdir(os.path.join(self.config_lz['work_dir'],'01-会员管理','会员资料')):
            if re.match(r'^MH\d{3}.*.xlsm$',fn):
                dic_li.append(fn.split('.')[0])
        return dic_li


    def ins_list(self):
        fn=os.path.join(self.config_lz['work_dir'],'03-教练管理','教练资料','教练信息.xlsx')
        df=pd.read_excel(fn,sheet_name='教练信息')
        ins_li=df['姓名'].tolist()

        return ins_li

    def  get_train_dic(self):
        fn=os.path.join(self.config_lz['work_dir'],'05-专业资料','训练项目.xlsx')
        df=pd.read_excel(fn,sheet_name='训练项目')
        df.fillna('',inplace=True)
        
        train_data={}
    
        train_data_by_action_name={}
        for index,row in df.iterrows():
            action_name=row['动作名称']
            form=row['形式']
            muscle = row['肌肉部位']
            category = row['动作大类']
            
            # 判断动作名称是否已经在字典中，若不在则添加
            if action_name not in train_data_by_action_name:
                train_data_by_action_name[action_name] = []
            
            # 将相关信息添加到对应动作名称的列表中
            train_data_by_action_name[action_name].append(form)
            train_data_by_action_name[action_name].append(muscle)
            train_data_by_action_name[action_name].append(category)

        train_data_by_muscle={}
        for index,row in df.iterrows():
            action_name=row['动作名称']
            form=row['形式']
            muscle = row['肌肉部位']
            category = row['动作大类']
            
            # 判断动作名称是否已经在字典中，若不在则添加
            if muscle not in train_data_by_muscle:
                train_data_by_muscle[muscle] = []
            
            # 将相关信息添加到对应动作名称的列表中
            train_data_by_muscle[muscle].append([form,category,action_name])   
            # train_data_by_muscle[muscle].append(form)
            # train_data_by_muscle[muscle].append(category)
            # train_data_by_muscle[muscle].append(action_name) 

        train_data_by_category={}
        for index,row in df.iterrows():
            action_name=row['动作名称']
            form=row['形式']
            muscle = row['肌肉部位']
            category = row['动作大类']
            
            # 判断动作名称是否已经在字典中，若不在则添加
            if category not in train_data_by_category:
                train_data_by_category[category] = []
            
            # 将相关信息添加到对应动作名称的列表中
            train_data_by_category[category].append([form,muscle,action_name])    
            # train_data_by_category[category].append(form)
            # train_data_by_category[category].append(muscle)
            # train_data_by_category[category].append(action_name)

        train_data['by_action_name']=train_data_by_action_name
        train_data['by_muscle']=train_data_by_muscle
        train_data['by_category']=train_data_by_category
        return train_data


    def  get_train_list_db(self):
        # fn=os.path.join(self.config_lz['work_dir'],'05-专业资料','训练项目.xlsx')
        # df=pd.read_excel(fn,sheet_name='训练项目')
        # df.fillna('',inplace=True)
        print('from llz database,get train item list.')
        conn=self.connect_mysql()
        cursor = conn.cursor()

        result={}

        # 获取收款人
        sql="select form,muscle,category,action_name from train_item_table"
        cursor.execute(sql)
        train_items = cursor.fetchall()
        cursor.close()
        conn.close()

        df=pd.DataFrame(train_items)
        df.columns=['形式','肌肉部位','动作大类','动作名称']
        # print(df)


        
        train_data={}
    
        train_data_by_action_name={}
        for index,row in df.iterrows():
            action_name=row['动作名称']
            form=row['形式']
            muscle = row['肌肉部位']
            category = row['动作大类']
            
            # 判断动作名称是否已经在字典中，若不在则添加
            if action_name not in train_data_by_action_name:
                train_data_by_action_name[action_name] = []
            
            # 将相关信息添加到对应动作名称的列表中
            train_data_by_action_name[action_name].append(form)
            train_data_by_action_name[action_name].append(muscle)
            train_data_by_action_name[action_name].append(category)

        train_data_by_muscle={}
        for index,row in df.iterrows():
            action_name=row['动作名称']
            form=row['形式']
            muscle = row['肌肉部位']
            category = row['动作大类']
            
            # 判断动作名称是否已经在字典中，若不在则添加
            if muscle not in train_data_by_muscle:
                train_data_by_muscle[muscle] = []
            
            # 将相关信息添加到对应动作名称的列表中
            train_data_by_muscle[muscle].append([form,category,action_name])   
            # train_data_by_muscle[muscle].append(form)
            # train_data_by_muscle[muscle].append(category)
            # train_data_by_muscle[muscle].append(action_name) 

        train_data_by_category={}
        for index,row in df.iterrows():
            action_name=row['动作名称']
            form=row['形式']
            muscle = row['肌肉部位']
            category = row['动作大类']
            
            # 判断动作名称是否已经在字典中，若不在则添加
            if category not in train_data_by_category:
                train_data_by_category[category] = []
            
            # 将相关信息添加到对应动作名称的列表中
            train_data_by_category[category].append([form,muscle,action_name])    
            # train_data_by_category[category].append(form)
            # train_data_by_category[category].append(muscle)
            # train_data_by_category[category].append(action_name)

        train_data_by_form={}
        for index,row in df.iterrows():
            action_name=row['动作名称']
            form=row['形式']
            muscle = row['肌肉部位']
            category = row['动作大类']
            
            # 判断动作名称是否已经在字典中，若不在则添加
            if form not in train_data_by_form:
                train_data_by_form[form] = []
            
            # 将相关信息添加到对应动作名称的列表中
            train_data_by_form[form].append([form,muscle,action_name])    
            # train_data_by_category[category].append(form)
            # train_data_by_category[category].append(muscle)
            # train_data_by_category[category].append(action_name)

        train_data['by_action_name']=train_data_by_action_name
        train_data['by_muscle']=train_data_by_muscle
        train_data['by_category']=train_data_by_category
        train_data['by_form']=train_data_by_form

        
        # return train_data
        # print(train_data)
        return jsonify(train_data)


    def  get_train_list(self):
        fn=os.path.join(self.config_lz['work_dir'],'05-专业资料','训练项目.xlsx')
        df=pd.read_excel(fn,sheet_name='训练项目')
        df.fillna('',inplace=True)
        
        train_data={}
    
        train_data_by_action_name={}
        for index,row in df.iterrows():
            action_name=row['动作名称']
            form=row['形式']
            muscle = row['肌肉部位']
            category = row['动作大类']
            
            # 判断动作名称是否已经在字典中，若不在则添加
            if action_name not in train_data_by_action_name:
                train_data_by_action_name[action_name] = []
            
            # 将相关信息添加到对应动作名称的列表中
            train_data_by_action_name[action_name].append(form)
            train_data_by_action_name[action_name].append(muscle)
            train_data_by_action_name[action_name].append(category)

        train_data_by_muscle={}
        for index,row in df.iterrows():
            action_name=row['动作名称']
            form=row['形式']
            muscle = row['肌肉部位']
            category = row['动作大类']
            
            # 判断动作名称是否已经在字典中，若不在则添加
            if muscle not in train_data_by_muscle:
                train_data_by_muscle[muscle] = []
            
            # 将相关信息添加到对应动作名称的列表中
            train_data_by_muscle[muscle].append([form,category,action_name])   
            # train_data_by_muscle[muscle].append(form)
            # train_data_by_muscle[muscle].append(category)
            # train_data_by_muscle[muscle].append(action_name) 

        train_data_by_category={}
        for index,row in df.iterrows():
            action_name=row['动作名称']
            form=row['形式']
            muscle = row['肌肉部位']
            category = row['动作大类']
            
            # 判断动作名称是否已经在字典中，若不在则添加
            if category not in train_data_by_category:
                train_data_by_category[category] = []
            
            # 将相关信息添加到对应动作名称的列表中
            train_data_by_category[category].append([form,muscle,action_name])    
            # train_data_by_category[category].append(form)
            # train_data_by_category[category].append(muscle)
            # train_data_by_category[category].append(action_name)

        train_data['by_action_name']=train_data_by_action_name
        train_data['by_muscle']=train_data_by_muscle
        train_data['by_category']=train_data_by_category
        
        # return train_data

        return jsonify(train_data)

##--------------------------------------------

    def get_template_info(self):
        fromWeb=request.data
        infos=self.read_template()
        return jsonify(infos)

    def get_template_info_db(self):
        # 创建一个游标对象
        print('from llz database,from template_info.')
        conn=self.connect_mysql()
        cursor = conn.cursor()

        result={}

        # 获取收款人
        sql="select cashier_id,cashier_name from cashier_table where account_status='on';"
        cursor.execute(sql)
        # cashiers_res = cursor.fetchall()
        cashiers=cursor.fetchall()
        # cashiers=[x[0] for x in cashiers_res]
        result['cashiers']=cashiers

        #获取课程类型
        sql="select distinct cls_type from cls_type_table where cls_status='on';"
        cursor.execute(sql)
        cls_types_res=cursor.fetchall()
        

        cls_types=[x[0] for x in cls_types_res]
        result['cls_types']=cls_types

         #获取课程类型、课程名称列表
        sql="select cls_type, cls_name from cls_type_table where cls_status='on';"
        cursor.execute(sql)
        cls_types_names_res=cursor.fetchall()
        cls_type_name_dic={}
        for tp,cls_name in cls_types_names_res:
            if tp not in cls_type_name_dic:
                cls_type_name_dic[tp]=[]
            if cls_name not in cls_type_name_dic[tp]:
                cls_type_name_dic[tp].append(cls_name)
        result['cls_type_name_dic']=cls_type_name_dic


        #获取收入类型
        sql="select income_type from income_type_table"
        cursor.execute(sql)
        income_type_res=cursor.fetchall()

        income_types=[x[0] for x in income_type_res]
        result['income_types']=income_types

        #获取客户来源类型
        sql='select source from cus_source_table'
        cursor.execute(sql)
        cus_source=cursor.fetchall()
        result['cus_source']=cus_source


        # print(result)
        # 关闭游标和连接
        cursor.close()
        conn.close()

        return jsonify(result)

    #遍历会员资料生成名字
    def get_cus_list(self):
        dic_li=self.cus_list()
        return jsonify(dic_li)

    def get_cus_list_db(self):
        print('from llz database, get cus list')
        conn=self.connect_mysql()
        cursor = conn.cursor()

        result={}

        # 获取收款人
        sql="select concat(cus_id,cus_name) from cus_info_table"
        cursor.execute(sql)
        cus_list_res = cursor.fetchall()
        cus_list=[x[0] for x in cus_list_res]
        # result['cus_list']=cus_list

        cursor.close()
        conn.close()

        return jsonify(cus_list)

        



    #获取教练信息
    def get_ins_list(self):
        ins_li=self.ins_list()
        return jsonify(ins_li)

    def get_ins_list_db(self):
         # 创建一个游标对象
        print('from llz database,get ins list.')
        conn=self.connect_mysql()
        cursor = conn.cursor()

        result={}

        # 获取教练姓名，不包括管理员角色
        sql="select ins_id,ins_name from ins_table where role='ins' and account_status='on';"
        cursor.execute(sql)
        ins_res = cursor.fetchall()
        ins_list=[x[0]+x[1] for x in ins_res]
        result['ins_list']=ins_list

        cursor.close()
        conn.close()

        return jsonify(result)
    
    def get_all_ins_list_db(self):
         # 创建一个游标对象
        print('from llz database,get ins list.')
        conn=self.connect_mysql()
        cursor = conn.cursor()

        result={}

        # 获取教练姓名，不包括管理员角色
        sql="select ins_id,ins_name from ins_table where role='ins';"
        cursor.execute(sql)
        ins_res = cursor.fetchall()
        ins_list=[x[0]+x[1] for x in ins_res]
        result['ins_list']=ins_list

        cursor.close()
        conn.close()

        return jsonify(result)

    def get_ins_list_nickname_db(self):
         # 创建一个游标对象
        print('from llz database,get ins nickname list.')
        conn=self.connect_mysql()
        cursor = conn.cursor()

        result={}

        # 获取教练姓名，不包括管理员角色
        sql="select ins_id,ins_name,COALESCE(ins_nickname,ins_name) as ins_nickname from ins_table where role='ins';"
        cursor.execute(sql)
        ins_res = cursor.fetchall()
        ins_nickname={}
        for item in ins_res:
            ins_nickname[item[0]+item[1]]=item[2]
        
        result['ins_nickname_list']=ins_nickname

        cursor.close()
        conn.close()

        return jsonify(result)

    # 定义前端页面路由
   
    def cus_infos(self):
        return render_template('cus_infos.html')



    @login_required
    def input_buy(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in')
        return render_template('input_buy.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def new_cus(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in') 
        return render_template('new_cus.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    @login_required
    def cus_cls_input(self):
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in') 
        return render_template('cus_cls_input.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

    def success(self):
        return render_template('success.html')

    def get_cus_info_db(self):
        data=request.json
        cus_id_name=data.get('selected_name')
        today=data.get('today')
        cus_id,cus_name=cus_id_name[:7],cus_id_name[7:]
        print('from llz database, get cus list')
        conn=self.connect_mysql()
        cursor = conn.cursor()

        result={}

        result['会员编码及姓名']=cus_id_name

        #限时课程不能转让，用cus_id查询上课的节数
        #常规课程可以转让或共享，用card_id查询上课的节数


        #获取课程类型、课程名称列表
        sql="select cls_type, cls_name from cls_type_table"
        cursor.execute(sql)
        cls_types_names_res=cursor.fetchall()
        cls_type_name_dic={}
        for tp,cls_name in cls_types_names_res:
            if tp not in cls_type_name_dic:
                cls_type_name_dic[tp]=[]
            if cls_name not in cls_type_name_dic[tp]:
                cls_type_name_dic[tp].append(cls_name)
        
        #根据课程类型生成相应的上课次数，购课节数，剩余节数，消费金额，购课次数
        #遍历课程类型
        total_cls_tkn_count=0
        total_pay=0
        for ctype in cls_type_name_dic.keys():
            for cls_name in cls_type_name_dic[ctype]:
                #这些项目的限时课程和常规课程sql一样

                #购课次数
                try:
                    sql='''select count(buy_date) as buy_count from buy_rec_table 
                            WHERE cus_id=%s and buy_type=%s and buy_cls_name=%s
                        '''
                        
                    cursor.execute(sql,(cus_id,ctype,cls_name))
                    buy_count=cursor.fetchall()
                    buy_count=buy_count[0][0]
                    if not buy_count:
                        buy_count=0
                except:
                    print(f'buy count {ctype} {cls_name} is null, set 0')
                    buy_count=0

                result['购课次数-'+ctype+'-'+cls_name]=buy_count

                #消费金额
                try:
                    sql='''select sum(real_pay) from buy_rec_table WHERE cus_id=%s and buy_type=%s and buy_cls_name=%s'''
                    cursor.execute(sql,(cus_id,ctype,cls_name))
                    cls_pay=cursor.fetchall()
                    cls_pay=cls_pay[0][0]
                    if not cls_pay:
                        cls_pay=0
                except:
                    cls_pay=0
                result['消费金额-'+ctype+'-'+cls_name]=cls_pay

                #计入总消费金额
                total_pay+=cls_pay

                #有些项目的限时课程和常规课程sql不一样
                if ctype=='限时课程':
                    #上课次数
                    sql='''
                        select sum(cls_long) as cls_tkn_count from cls_tkn_rec_table 
                        WHERE card_id in 
                            (select card_id from cards_table where
                            card_id in 
                                (select card_id from cardholder_card_table where cus_id=%s)
                            and card_type=%s 
                            and card_name=%s 
                            )
                            and cus_id=%s;
            
                    '''
                    current_time=datetime.datetime.now().strftime('%Y-%m-%d')
                    try:
                        cursor.execute(sql,(cus_id,ctype,cls_name,cus_id))
                        cls_tkn_count=cursor.fetchall()
                        cls_tkn_count=cls_tkn_count[0][0]
                        if not cls_tkn_count:
                            cls_tkn_count=0
                    except Exception as e:
                        print('get cls_tkn_count_lmt_sj:',e)
                        cls_tkn_count=0
                    #计入总上课次数
                    total_cls_tkn_count+=cls_tkn_count

                    result['上课次数-'+ctype+'-'+cls_name]=cls_tkn_count
                
                if ctype=='常规课程':
                    #上课次数
                    sql='''
                        select sum(cls_long) from cls_tkn_rec_table 
                        where card_id in (select card_id from cards_table 
                                            where card_id in (select card_id from cardholder_card_table where cus_id=%s) 
                                            and card_type=%s
                                            and card_name=%s)
                                and cus_id=%s;
                    '''
                    try:
                        cursor.execute(sql,(cus_id,ctype,cls_name,cus_id))
                        cls_tkn_count=cursor.fetchall()
                        cls_tkn_count=cls_tkn_count[0][0]
                        if not cls_tkn_count:
                            cls_tkn_count=0
                    except Exception as e:
                        print('get cls_tkn_count_cg:',e)
                        cls_tkn_count=0

                    #计入总上课次数
                    total_cls_tkn_count+=cls_tkn_count

                    result['上课次数-'+ctype+'-'+cls_name]=cls_tkn_count

                    #购课节数（仅常规课程有，9.9元私教还需要筛选时间，限时课程不计此项）
                    if cls_name=='9.9元私教':
                        sql='''
                            SELECT
                                'total cg 9.9 cls count' AS item,
                                SUM(result.cls_qty) AS cls_qty
                            FROM
                                (
                                SELECT
                                    card_id,
                                    SUM(cls_qty) AS cls_qty
                                FROM
                                    (
                                    SELECT
                                        buy_flow_id,
                                        MIN(card_id) AS card_id,
                                        MIN(buy_num) AS cls_qty
                                    FROM
                                        buy_rec_table
                                    WHERE
                                        card_id IN(
                                        SELECT
                                            cardholder_card_table.card_id
                                        FROM
                                            cardholder_card_table
                                        LEFT JOIN cards_table ON cards_table.card_id = cardholder_card_table.card_id
                                        WHERE
                                            cardholder_card_table.cus_id = %s AND cards_table.end_time >= %s
                                    ) AND buy_type = %s AND buy_cls_name = %s
                                GROUP BY
                                    buy_flow_id
                                ) AS subq
                            GROUP BY
                                card_id
                            ) AS result;
                        '''
                        cursor.execute(sql,(cus_id,today,ctype,cls_name))
                    else:
                        sql='''
                            SELECT
                                'total cg cls count' AS item,
                                SUM(result.cls_qty)
                            FROM
                                (
                                SELECT
                                    card_id,
                                    SUM(cls_qty) AS cls_qty
                                FROM
                                    (
                                    SELECT
                                        buy_flow_id,
                                        MIN(card_id) AS card_id,
                                        MIN(buy_num) AS cls_qty
                                    FROM
                                        buy_rec_table
                                    WHERE
                                        card_id IN(
                                        SELECT
                                            card_id
                                        FROM
                                            cardholder_card_table
                                        WHERE
                                            cus_id = %s
                                    ) AND buy_type = %s AND buy_cls_name = %s
                                GROUP BY
                                    buy_flow_id
                                ) AS subq
                            GROUP BY
                                card_id
                            ) AS result
                        '''
                        cursor.execute(sql,(cus_id,ctype,cls_name))
                    try:
                        buy_num=cursor.fetchone()[1]
                        # buy_num_cg_sj=buy_num_cg_sj[0]
                        if not buy_num:
                            buy_num=0
                    except:
                        print(f'buy num {ctype} {cls_name} is null, set 0')
                        buy_num=0

                    result['购课节数-'+ctype+'-'+cls_name]=float(buy_num)
                    # print(buy_num)

                    # result['剩余节数-'+ctype]=0
                    try:
                        result['剩余节数-'+ctype+'-'+cls_name]=result['购课节数-'+ctype+'-'+cls_name]-result['上课次数-'+ctype+'-'+cls_name]
                        # result['剩余节数-'+ctype]+=result['剩余节数-'+ctype+'-'+cls_name]
                    except:
                        print(f'remain cls num {ctype} {cls_name} is null, set 0')
                        result['剩余节数-'+ctype+'-'+cls_name]=0
                        # result['剩余节数-'+ctype]=0
            
                    # print('buy cg cls:',ctype,'  ',cls_name,'  ',result['购课节数-'+ctype+'-'+cls_name])
                    # print('taken cg cls:',ctype,'  ',cls_name,'  ',result['上课次数-'+ctype+'-'+cls_name])
                    # print('remain cg cls:',ctype,'  ',cls_name,'  ',result['剩余节数-'+ctype+'-'+cls_name])

        # 限时课程到期日
        sql=f'''
        select card_id,max(end_time) as max_lmt_time from cards_table 
        where card_id in (select card_id from cardholder_card_table where cus_id=%s) and card_type='限时课程' GROUP by card_id

        '''
        cursor.execute(sql,cus_id)
        try:
            max_date=cursor.fetchone()[1]
            print('max_date',max_date)  
        except:
            max_date='' 

        # 限时课程是否有效
        if max_date:
            if datetime.datetime.now().date()<=max_date:
                result['限时课程是否有效']='是'
            else:
                result['限时课程是否有效']='否'
        else:
            result['限时课程是否有效']='否'

        if not max_date:
            max_date='-'
        result['限时课程到期日']=max_date



        #平均每单消费金额
        sql=f"select sum(real_pay)/count(real_pay) as avr_pay from buy_rec_table WHERE cus_id='{cus_id}'"
        cursor.execute(sql)
        avr_pay=cursor.fetchall()
        avr_pay=avr_pay[0][0]
        if not avr_pay:
            avr_pay=0

        result['平均每单消费金额']=float(avr_pay)

        #最后一次购课日期
        sql=f"select max(buy_date) from buy_rec_table WHERE cus_id='{cus_id}'"
        cursor.execute(sql)
        latest_buy_date=cursor.fetchall()
        latest_buy_date=latest_buy_date[0][0]
        if latest_buy_date:
            latest_buy_date=latest_buy_date.strftime('%Y-%m-%d')
        else:
            latest_buy_date='-'

        result['最后一次购课日期']=latest_buy_date

        #开始上课日期
        sql=f"select min(cls_datetime) from cls_tkn_rec_table WHERE cus_id='{cus_id}'"
        cursor.execute(sql)
        first_cls_tkn_date=cursor.fetchall()
        first_cls_tkn_date=first_cls_tkn_date[0][0]
        if first_cls_tkn_date:
            first_cls_tkn_date=first_cls_tkn_date.strftime('%Y-%m-%d')
        else:
            first_cls_tkn_date='-'

        result['开始上课日期']=first_cls_tkn_date


        #最后一次上课日期
        sql=f"select max(cls_datetime) from cls_tkn_rec_table WHERE cus_id='{cus_id}'"
        cursor.execute(sql)
        latest_cls_tkn_date=cursor.fetchall()
        latest_cls_tkn_date=latest_cls_tkn_date[0][0]
        if latest_cls_tkn_date:
            latest_cls_tkn_date=latest_cls_tkn_date.strftime('%Y-%m-%d')
        else:
            latest_cls_tkn_date='-'

        result['最后一次上课日期']=latest_cls_tkn_date

        #上课总天数
        sql=f"select datediff(CURDATE(),min(cls_datetime))+1 as total_cls_tkn_days from cls_tkn_rec_table WHERE cus_id='{cus_id}'"
        cursor.execute(sql)
        total_cls_tkn_days=cursor.fetchall()
        total_cls_tkn_days=total_cls_tkn_days[0][0]
        if not total_cls_tkn_days:
            total_cls_tkn_days=0

        result['上课总天数']=total_cls_tkn_days        
        

        #上课总次数
        try:
            result['上课总次数']=total_cls_tkn_count
        except Exception as e:
            print('calculate total cls tkn qty error, set 0 :',e)
            result['上课总次数']=0
        
        #总消费金额
        try:
            result['总消费金额']=total_pay
        except Exception as e:
            print('calculate total pay error, set 0 :',e)
            result['总消费金额']=0


        #上课频率
        if total_cls_tkn_days!=0:
            cls_frqcy=result['上课总次数']*7/total_cls_tkn_days
        else:
            cls_frqcy=0
        result['上课频率']=cls_frqcy


        #最近一次体测结果
        body_history=self.get_lst_body_history_db(cus_id_name,jsonify='no')

        result.update(body_history)

        cursor.close()
        conn.close()

        # print('客户综合信息:',result)

        return jsonify(result)
    
    def deal_get_cus_basic_info(self):
        data=request.json
        cus_id=data['cusIdName'][:7]
        cus_name=data['cusIdName'][7:]
        try:
            conn=self.connect_mysql()
            cursor=conn.cursor()
            sql='''
                select cit.cus_id,cit.cus_name,birthday,sex,TIMESTAMPDIFF(YEAR, birthday, CURDATE()) AS age,it.industry_name,ot.occu_name,pic_src,source,
                    mobile_phone,COALESCE(emgc_contact,'') AS emgc_contact,COALESCE(emgc_mobile,'') AS emgc_mobile,COALESCE(addr,'') AS addr,
                    icct.ins_ids,icct.ins_names
                from cus_info_table cit
                left join(
                    select industry_id,industry_name from industry_table
                ) as it on it.industry_id=cit.industry_id
                left join(
                    select occu_id,occu_name from occu_table
                ) as ot on ot.occu_id=cit.occu_id
                left join ins_control_cus_table icct on icct.cus_id=cit.cus_id
                where cit.cus_id=%s;
            '''
            cursor.execute(sql,cus_id)
            res=cursor.fetchone()
            cursor.close()
            conn.close()
            if res:
                return jsonify({'res':'ok','data':res})
            else:
                return jsonify({'res':'failed','error':'no data'})
        except Exception as e:
            print('get cus info data error:',e)
            cursor.close()
            conn.close()
            return jsonify({'res':'failed','error':e})

    
    def get_cus_cards_by_type(self):
        print('get card ids by type....')
        data=request.json
        cus_id=data['cus_id_name'][:7]
        cus_name=data['cus_id_name'][7:]
        buy_type=data['buy_type']
        buy_date=data['buy_date']
        buy_cls_name=data['buy_cls_name']

        
        #input_buy.html页面上默认选择是“限时课程”,“私教”
        if buy_type=='':
            buy_type='限时课程'
            buy_cls_name='私教'
        
        # print(buy_cls_name)
        
        conn=self.connect_mysql()
        cursor=conn.cursor()

        # 获取课程代表字母
        sql='select cls_type,cls_name,cls_letter from cls_type_table'
        cursor.execute(sql)
        letters=cursor.fetchall()
        cls_ltr_dic={}
        for ltrs in letters:
            cls_ltr_dic[ltrs[0]+'-'+ltrs[1]]=ltrs[2]
        
        try:
            #长期常规课程
            if buy_type=='常规课程':    
                post_fix=cls_ltr_dic[buy_type+'-'+buy_cls_name]
                sql='''
                    select distinct card_id from cards_table 
                    where card_id in (select card_id from cardholder_card_table where cus_id=%s)
                    and card_type=%s
                    and card_name=%s
                    and card_start_time<=%s 
                    and end_time>=%s
                '''
                cursor.execute(sql,(cus_id,buy_type,buy_cls_name,buy_date,buy_date))
                res=cursor.fetchall()
                if res:
                    pass
                #如果不存在卡号，新生成卡号
                else:
                    print('get no cus card id,generate new 常规课程 card id')
                    cr_time=datetime.datetime.now().strftime('%Y%m%d%H%M%S')
                    new_card_id=self.config_lz['cus_id_prefix']+post_fix+cr_time
                    res=[new_card_id]
              

            #限时课程
            elif buy_type=='限时课程':

                post_fix=cls_ltr_dic[buy_type+'-'+buy_cls_name]

                cr_time=datetime.datetime.now().strftime('%Y%m%d%H%M%S')    
                new_card_id=self.config_lz['cus_id_prefix']+post_fix+cr_time
                res=[new_card_id]

            return jsonify({'res':'ok','card_ids':res})

        except Exception as e:
            print('get cus card ids failed: ',e)
            return jsonify({'res':'failed'})
        finally:
            cursor.close()
            conn.close()


        

    def get_lst_body_history_db(self,cus_id_name,jsonify='yes'):
        if not cus_id_name:
            cus_id_name=request.text.decode('utf-8')
        cus_id,cus_name=cus_id_name[:7],cus_id_name[7:]
        conn=self.connect_mysql()
        cursor = conn.cursor()
        #获取最近一次的体测数据
        sql=f"select * from body_msr_table WHERE cus_name='{cus_name}' and cus_id='{cus_id}' order by msr_date DESC limit 1"
        cursor.execute(sql)
        lst_body_history=cursor.fetchall()

        result={}
        if lst_body_history:
            for key,item in enumerate(['id','cus_id','cus_name','msr_date','ht','wt','bfr','chest','l_arm','r_arm','waist','hip','l_leg','r_leg','l_calf','r_calf','heart','balance','power','flex','core','body_problems']):
                try:
                    result[item]=float(lst_body_history[0][key])
                except:
                    result[item]=lst_body_history[0][key]
            #修改键值名返回前端
            result['lst_msr_date']=result.pop('msr_date')
            result['lst_msr_date']=result['lst_msr_date'].strftime('%Y-%m-%d')
            result.pop('id')
            result.pop('cus_id')
            result.pop('cus_name')

            #获取体检日期列表
            sql=f"select msr_date from body_msr_table WHERE cus_name='{cus_name}' and cus_id='{cus_id}'"
            cursor.execute(sql)
            msr_dates=cursor.fetchall()
            msr_dates=[x[0].strftime('%Y-%m-%d') for x in msr_dates]
            msr_dates_txt='\n'.join(msr_dates)
            result['msr_dates']=msr_dates_txt

            #获取体测次数
            sql=f"select count(msr_date) from body_msr_table WHERE cus_name='{cus_name}' and cus_id='{cus_id}'"
            cursor.execute(sql)
            msr_num=cursor.fetchall()
            msr_num=msr_num[0][0]
            result['msr_num']=msr_num

        else:
            result={'lst_msr_date': '-', 'msr_num': '-', 
            'msr_dates': '-', 'bfr': '-', 'ht': '-', 'wt': '-', 'waist': '-', 
            'chest': '-', 'l_arm': '-', 'r_arm': '-', 'hip': '-', 'l_leg': '-', 
            'r_leg': '-', 'l_calf': '-', 'r_calf': '-', 
            'heart': '-', 'balance': '-', 'power': '-', 
            'flex': '-', 'core': '-'}

        if jsonify=='yes':
            cursor.close()
            conn.close()
            return jsonify({'lst_msr_date':result})
        else:
            return result
       



    def open_cus_fn(self):
        cus_name=request.data.decode('utf-8')
        cus_li=self.cus_list()
        if cus_name and cus_name in cus_li:
            work_dir=self.wecom_dir()
            fn=os.path.join(work_dir,cus_name+'.xlsm')
            # os.startfile(fn)
            return f'正在打开 {cus_name} 的会员档案'
        else:
            return '会员编码及编码为空/无此会员档案'

    def _get_max_code(self):        
        cus_li = self.cus_list()
        cus_num = [int(x[2:5]) for x in cus_li]
        max_num = max(cus_num)
        new_num = max_num + 1
        return str(new_num).zfill(3)


    def check_new(self):
        # dat=request.data.decode('utf-8')        
        # with self.app_lock:
        txt_num=self._get_max_code()            
        print('新客户自动编号：',txt_num)           
            
        return txt_num

    
    def generate_new(self):
        with self.app_lock:
            txt_num=self.check_new()
            print('generating new',txt_num)
            try:
                fn_in=request.data
                fn='MH'+txt_num+fn_in.decode('utf-8')
                print(fn)
                fn,trial_cus_name,sex,birthMonth,cusSource,dvc=fn.split('|')
                work_dir=self.wecom_dir()
                tplt_dir=os.path.dirname(work_dir)
                new_fn=os.path.join(work_dir,fn+'.xlsm')

                app=xw.App(visible=False)
                wb=app.books.open(os.path.join(tplt_dir,'模板.xlsm'))
                sht=wb.sheets['基本情况']
                sht['A2'].value=fn[0:5]
                sht['B2'].value=fn[5:]
                sht['D2'].value=sex
                sht['E2'].value=birthMonth
                sht['F2'].value=cusSource
                if len(fn[5:])>1:
                    sht['C2'].value=fn[5:][1:]
                else:
                    sht['C2'].value=fn[5:]

                wb.save(new_fn)
                wb.close()
                app.quit()

                if trial_cus_name:
                    self.write_deal_cus_name_to_trial_table(formal_cus_name=fn,trial_cus_name=trial_cus_name)

                return fn
            except Exception as e:
                self.release_event.set()
                return e
        
    def generate_new_db(self):
        with self.app_lock:            
            conn=self.connect_mysql()
            cursor=conn.cursor()
            sql=f"SELECT max(cast(substring(cus_id,3) as UNSIGNED)) FROM cus_info_table;"
            cursor.execute(sql)
            max_id=cursor.fetchall()[0][0]
            if max_id: 
                txt_num=str(max_id+1).zfill(5)
            #第一个客户的id
            else:
                txt_num='00001'
            try:
                dataRequest=request.json
     
                fn=self.cus_id_prefix+txt_num+dataRequest['cusName']
                # fn='MH00220王测试|王测试|女|199008|小红书|pc'
                data={}  
                data['trial_cus_name']=dataRequest['trialCusName']
                data['sex']=dataRequest['gender']
                data['mobile_phone']=dataRequest['mobilePhone']
                data['birthday']=dataRequest['birthDay']
                data['source']=dataRequest['cusSource']
                data['dvc']=dataRequest['dvc']
                data['cus_id']=self.cus_id_prefix+txt_num
                data['cus_name']=dataRequest['cusName']
                data['nick_name']=data['cus_name'] if len(data['cus_name'])<2 else data['cus_name'][1:]
                data['birthday_type']='ymd'
                ins_ids=dataRequest['insIds']
                data['industry_id']=dataRequest['industry_id']
                data['occu_id']=dataRequest['occu_id']
                data['emgc_contact']=dataRequest['emgc_contact']
                data['emgc_mobile']=dataRequest['emgc_mobile']
                data['addr']=dataRequest['addr']
                # data['birthday_type']='ym'
                # trial_cus_name=data['trial_cus_name']
                cus_id_name=data['cus_id']+data['cus_name']
                data['cmt']=dataRequest['cmt']
                data['add_datetime']=dataRequest['add_datetime']

                data_col=['cus_id','cus_name','nick_name','sex','mobile_phone','birthday','birthday_type','industry_id','occu_id','source','emgc_contact','emgc_mobile','addr','add_datetime','cmt']
                sorted_data={key: data[key].strip() for key in data_col}
                values=tuple(sorted_data.values())
                today=datetime.datetime.now().strftime('%Y-%m-%d')
                # print(sorted_data)

                #生成主管教练的姓名及教练ID
                ins_name_arr=[]
                ins_id_arr=[]
                with conn.cursor(cursor=pymysql.cursors.DictCursor) as cursor:
                    try:
                        for ins_id in ins_ids.split(','):
                            sql='''
                                select ins_id,ins_name from ins_table 
                                where ins_id=%s
                            '''
                            cursor.execute(sql,(ins_id,))
                            fetch_ins=cursor.fetchone()
                            ins_name_arr.append(fetch_ins['ins_name'])
                            ins_id_arr.append(fetch_ins['ins_id'])
                        ins_names_str=','.join(ins_name_arr)
                        ins_ids_str=','.join(ins_id_arr)                        
                    except Exception as e:
                        print(e) 
                        return jsonify({'res':'failed','error':f'error:{e}'})
                # except Exception as e:
                #     print(e)
                #     return jsonify({'res':'failed','error':f'error:{e}'})
                # finally:
                #     cursor.close()
                #     conn.close()

                try:
                    cursor=conn.cursor()
                    #新增会员
                    sql='''
                            insert into  cus_info_table (cus_id,cus_name,nick_name,sex,mobile_phone,birthday,birthday_type,industry_id,occu_id,source,emgc_contact,emgc_mobile,addr,add_datetime,cmt) 
                            values
                            (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
                        '''
                    cursor.execute(sql,values)


                    #写入教练主管会员表
                    sql='''
                            insert into ins_control_cus_table (cus_id,cus_name,ins_ids,ins_names) 
                            values
                            (%s,%s,%s,%s)
                        '''
                    cursor.execute(sql,(data['cus_id'],data['cus_name'],ins_ids_str,ins_names_str))

                    #在体验课表写入体验课出单会员信息
                    if data['trial_cus_name']:
                        sql=f'''
                            update trial_cls_table 
                            set deal_yn='是',deal_date=%s,formal_cus_id_name=%s                        
                            where trial_cus_name=%s order by 'id' Desc LIMIT 1
                            '''
                        cursor.execute(sql,[today,cus_id_name,data['trial_cus_name']])

                        sql='''
                            insert into ins_control_cus_table 
                            
                        '''

                    conn.commit()  
                except Exception as e:
                    print(e)
                    return jsonify({'res':'failed','error':f'error:{e}'})
                

            except Exception as e:
                print('write into new cus error:',e)
                return jsonify({'res':'failed','error':f'error:{e}'})
            finally:
                cursor.close()
                conn.close()                
            
        
            return jsonify({'res':'ok','cus_name':f'{cus_id_name}'})

    def write_deal_cus_name_to_trial_table(self,formal_cus_name,trial_cus_name):
        try:
            fn=os.path.join(self.config_lz['work_dir'],'03-教练管理','体验课上课记录表.xlsx')
            app=xw.App(visible=False)
            wb=app.books.open(fn)
            sht=wb.sheets['体验课上课记录表']
            e_column_data=sht.range('e1').expand('down').value

            for row_index, value in enumerate(e_column_data, start=1):
                if value == trial_cus_name:
                    # 根据条件设置 M 列的值为 'AA'
                    sht.range(f'M{row_index}').value = formal_cus_name

            wb.save(fn)
            wb.close()
            app.quit()
        except Exception as e:
            print('写入体验课表错误：',e)
            return '写入体验课表错误：'+e



    def welcome(self):
        print('welcome')
        return jsonify({'res':'关于我们页面','res2':'这个是welcome页面KK'})
    
    @login_required
    def test_page(self):
        session_logged_in=session.get('logged_in')
        session_name=session.get('user')
        session_role=session.get('role')
        session_id=session.get('ins_id')
        session_logged_in=session.get('logged_in') 
        return render_template('test.html',session_ins_name=session_name,session_role=session_role,session_ins_id=session_id,session_logged_in=session_logged_in)

class Vividict(dict):
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

class FERROR(Exception):
    pass

if __name__ == '__main__':
    app = LzService(__name__)
    app.secret_key='lz8888'
    if len(sys.argv)>1:
        print(f'服务器为：{sys.argv[1]}:5000')
        app.run(debug=True,host=sys.argv[1],port=5000)
    else:
        app.run(debug=True)

    # with app.app_context():
    #     app.get_all_continue_buy_cls_rec(data={'today':'2024-09-29'})
    # app.run(debug=True,host='192.168.158.71',port=5000)
    # app.run(debug=True,host='192.168.10.2',port=5000)
    # app.run(debug=True,host='192.168.1.41',port=5000)
    # app.run(debug=True,host='192.168.1.149',port=5000)
    # res=wecom_dir()
    # print(res)
